네이버 클라우드 부트캠프/복습 정리

21일차 Java [ 데이터베이스 입출력, DB MySQL과 eclipse 연결 ]

유정♡ 2024. 3. 20. 11:29

[ JDBC 개요 ]

자바는 데이터베이스(DB)와 연결해서 데이터 입출력을 작업할 수 있더럭 JDBC(Java Database Connecitivity) 라이브러리(java.sql 패키지)를 제공한다. JDBC는 데이터베이스 관리시스템(DBMS)의 종류와 상관없이 동일하게 사용할 수 있는 클래스와 인터페이스로 구성되어 있다.

 

JDBC 인터페이스를 통해 실제로 DB와 작업하는 것은 JDBC Driver이다. JDBC Driver는 JDBC 인터페이스를 구현한 것으로, DBMS마다 별도로 다운로드 받아 사용해야 한다.

 

☆★ JDBC에 포함되어 있는 클래스와 인터페이스들의 연관관계 ☆★

※ DriverManager : DriverManager 클래스는 JDBC Driver를 관리하며 DB와 연결해서 Connection 구현 객체를 생성한다.

※ Connection : Connection 인터페이스는 Statement, PreparedStatement, CallableStatement 구현 객체를 생성하며, 트랜잭션(Transaction)처리 및 DB 연결을 끊을 때 사용한다.

※ Statement : Statement  인터페이스는 SQL의 DDL(Data Definition Language)와 DML(Data Mainpulation Language)을 실행할 때 사용한다. 주로 변경되지 않는 정적 SQL 문을 실행할 때 사용한다.

※ PreparedStatement : PreparedStatement는 Statement와 동일하게 SQL의 DDL, DML 문을 실행할 때 사용한다. 차이점은 매개변수화된 SQL문을 사용할 수 있기 때문에 편리성과 보안성이 좋다. 그래서 Statement보다는 PreparedStatement를 주로 사용한다.

CallableStatement : CallableStatement는 DB에 저장되어 있는 프로시저(procuder)와 함수(function)를 호출할 때 사용한다. ( 잘 쓰이지 않음 )

※ ResultSet : ResultSet은 DB에서 가져온 데이터를 읽을 때 사용한다.

 

 

[ DB 연결 ]

클라이언트 프로그램에서 DB와 연결하려면 해당 DBMS의 JDBC Driver가 필요하다. 또한 연결에 필요한 다음 네가지 정보가 있어야 한다.

연결에 필요한 다음 네가지 정보

IP 주소는 컴퓨터를 찾아가기 위해, Port 번호는 DBMS로 연결하기 위해 필요하다. DBMS는 여러개의 DB를 관리하므로 실제로 사용할 DB 이름이 필요하며, 어떤 사용자인지 인증받기 위한 계정 및 비밀번호도 필요하다.

 

 

 

[ JDBC Driver 설치 ]

MySQL :: Download Connector/J

 

MySQL :: Download Connector/J

MySQL Connector/J is the official JDBC driver for MySQL. MySQL Connector/J 8.0 and higher is compatible with all MySQL versions starting with MySQL 5.7. Additionally, MySQL Connector/J 8.0 and higher supports the new X DevAPI for development with MySQL Ser

dev.mysql.com

 

[ Archives -> Operating System : Platform Independent 선택 ( 윈도우환경 ) -> 밑에 ZIP Archive 파일 다운로드 -> 압축풀기 -> mysql-connector-j-8.2.0 확인 ]

압축 풀 때 파일 이름이 너무 길면 오류가 발생할 수도 있음 !!

 

[ 이클립스 eclipse 실행 -> DB와 연결할 프로젝트 오른쪽 클릭 -> Build Path -> Configure Build Path -> Libraries -> Modulepath -> Add External JARs... -> mysql-connector-j-8.2.0 선택후 열기 클릭 -> 프로젝트에 Referenced Libraries 잘 생성 됐는지 확인 !! ]

 

 

[ DB 연결 ]

클라이언트 프로그램을 DB와 연결하기 위해 가장 먼저 해야 할 작업은 JDBC Driver를 메모리로 로딩하는 것이다.

Class.forName() 메소드는 문자열로 주어진 JDBC Driver 클래스를 Build Path에서 찾고, 메모리로 로딩한다.

Class.forName("com.mysql.cj.jdbc.Driver");   // MySQL
               // Class 클래스로 MySQL 드라이버를 로딩하는 코드 => DriverManager에 등록됨

Class.forName("oracle.jdbc.OracleDriver);    // Oracle용

 

이 과정에서 JDBC Driver 클래스의 static 블록이 실행되면서 DriverManager에 JDBC Driver 객체를 등록하게 된다. 만약 Build Path에서 JDBC Driver 클래스를 찾지 못하면 ClassNotFoundException이 발생하므로 예외 처리를 해야 한다. DriverManager에 JDBC Driver가 등록되면 getConnection() 메소드로 DB와 연결할 수 있다.

Connection con = DriverManager.getConnection(연결 문자열 url ,사용자 id,비밀번호 pw);

 

첫번째 매개값은 연결 문자열인데, DBMS마다 다른 형식을 가지고 있다.

String url="jdbc:mysql//localhost:3306/yujung"; -> MySQL

"jdbc:oracle:thin:@localhost(IP주소):3306(Port)/yujung(DB명)"; -> Oracle

 

연결이 성공하면 getConnection() 메소드는 Connection 객체를 리턴한다. 만약 연결이 실패하면 SQLException이 발생하므로 예외 처리를 해야 한다.

 

성공했던 DB 연결을 끊을 때에는 Connection 객체의 close() 메소드를 호출한다. 이 메소드는 SQLException이 발생할 수 있으므로 예외 처리가 필요하다. ( 연결을 끊고 싶을 때는 con.close(); )

import java.sql.Connection;
import java.sql.DriverManager;

public class Test {	
	public static Connection get() {
		Connection con=null;
		
		try {
			String id="root";
			String pw="1234";
			String url="jdbc:mysql://localhost:3306/yujung";
			
			Class.forName("com.mysql.cj.jdbc.Driver");
			//Class 클래스로 mysql 드라이버를 로딩하는 코드 => DriverManager에 등록됨
			
			con=DriverManager.getConnection(url,id,pw);
			//Connection객체를 얻음
			//con은 실제로 데이터베이스와 연결하여 작업을 수행할 수 있는 통로롤 작용하는
			//중요한 객체 변수로 사용된다!!!!
			
			System.out.println("데이터베이스에 연결됐다");
		}
		catch(Exception e) {
			System.out.println("로딩실패");
		}
		return con;
	}
}

 

 

[ 데이터 저장 ]

매개변수화된 SQL 문을 실행하려면 PreparedStatement가 필요하다. 다음과 같이 Conection의 prepateStatement() 메소드로부터 PreparedStatement를 얻는다.

PreparedStatement pstmt = con.prepareStatement(sql);

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Test2 {
	public static void main(String[] args) throws SQLException {
		
		Connection con=null;  //DB와 연결하는 인터페이스
		PreparedStatement psmt=null; //sql문 객체
		ResultSet rs=null;  //sql에 대한 반환 (쿼리 실행에 대한 결과값 저장)
		
		try {
			String que="select *from emp";
			
			con=Test.get(); //DB연결
			
			psmt=con.prepareStatement(que);
			rs=psmt.executeQuery();  
			
			//select -> executeQuery()
			//DML(insert, update, delete) -> executeUpdate();
			while(rs.next()) {
				//DB에 있는 값들을 가져옴(행 기준)
				String empno=rs.getString(1);
				String ename=rs.getString(2);
				String job=rs.getString(3);
				int mgr=rs.getInt(4);
				
				java.sql.Date hiredate=rs.getDate(5);
				int sal=rs.getInt(6);
				int comm=rs.getInt(7);
				int deptno=rs.getInt(8);
				
				System.out.println(empno+" "+ename+" "+job);
				
			}
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		rs.close();
		psmt.close();
		con.close();
	}
}

 

 

[ 데이터 읽기 ]

PrepareStatement를 생성할 때 SQL 문이 INSERT, UPDATE, DELETE일 경우에는 executeUpdate() 메소드를 호출해야 하지만, 데이터를 가져오는 SELECT문일 경우에는 executeQuery() 메소드를 호출해야한다. executeQuery() 메소드는 가져온 데이터를 ResultSet에 저장하고 리턴한다.

ResultSet rs = pstmt.executeQuery();

 

※ ResultSet 구조 : ResultSet 은 SELECT문에 기술된 컬럼으로 구성된 행(row)의 집합이다. 예를 들어 다음 SELET문은 userid, username, userage 컬럼으로 구성된 ResultSet을 리턴한다.

SELECT userid, username, userage FROM users

 

위의 SELECT문이 가져온 데이터 행이 4개라면 ResultSet의 내부 구조는 다음과 같다.

ResultSet의 특징은 커서(cursor)가 있는 행의 데이터만 읽을 수 있다는 것이다. 여기서 커서는 행을 가리키는 포인터를 말한다. ResultSet에는 실제 가져온 데이터 행의 앞과 뒤에 beforeFirst 행과 afterLast 행이 붙는데, 최초 커서는 beforeFirst를 가리킨다. 따라서 첫 번째 데이터 행인 First 행을 읽으려면 커서를 이동시켜야 한다. 이때 next() 메소드를 사용한다. 

boolean result = rs.next();

 

next() 메소드는 커서를 다음 행으로 이동시키는데, 이동한 행에 데이터가 있으면 ture를 없으면 false를 리턴한다. ( 값이 없을 때 까지 데이터를 가져 옴 ) 앞의 그림을 보면 last 행까지는 true를 리턴하고, afterLast 행으로 이동하려면 false를 리턴하는 것을 볼 수 있다.

 

 

[ 프로시저와 함수 호출 ]

JDBC에서 프로시저와 함수를 호출할 때는 CallableStatement를 사용한다.

// 프로시저를 호출할 경우
String sql = "{ call 프로시저명(?, ?, ...) } ";
CallableStatement cstmt = con.prepareCall(sql);

// 함수를 호출할 경우
String sql = "{ ? = call 함수명(?, ?, ...) } ";
CallableStatement cstmt = con.prepareCall(sql);

 

[ 트랜잭션 처리 ]

트랜잭션(transaction)은 기능 처리의 최소 단위를 말한다. 하나의 기능은 여러 가지 소작업들로 구성된다. 최소 단위라는 것은 이 소작업들을 분리할 수 없으며, 전체를 하나로 본다는 개념이다. 트랜잭션은 소작업들이 모두 성공하거나 실패해야 한다.예를 들어 계좌 이체는 출금 작업과 입금 작업으로 구성된 트랜잭션이다. 출금과 입금 작업 중 하나만 성공할 수 없으며, 모두 성공하거나 모두 실패해야한다. 계좌이체는 DB 입장에서 보면 두 개의 계좌 금액을 수정하는 작업이다. 출금 계좌에서 금액을 감소 시키고, 입금 계좌에서 금액을 증가시킨다. 따라서 다음과 같이 두 개의 UPDATE문이 필요하다. 두 UPDATE문은 모두 성공하거나 모두 실패해야 하며, 하나만 성공할 수 없다.

계좌 이체 (트랜잭션)
// 츨금 작업
UPDATE accounts SET balance = balance - 이체금액 WHERE ano = 출금계좌번호

// 입금 작업
UPDATE accounts SET balance = balance + 이체금액 WHERE ano = 입금계좌번호

 

DB는 트랜잭션을 처리하기 위해 커밋(commit)과 롤백(rollback)을 제공한다. 커밋은 내부 작업을 모두 성공 처리하고, 롤백은 실행 전으로 돌아간다는 의미에서 모두 실패 처리한다. JDBC에서는 INSERT, UPDATE, DELETE 문을 실행할 때마다 자동 커밋이 일어난다. 이기능은 계좌 이체와 같이 두 가지 UPDATE 문을 실행할 때 마다 자동 커밋이 일어난다. 이 기능은 계좌이체와 같이 두가지 UPDATE문을 실행할 때 문제가 된다. 출금 작업이 성공되면 바로 커밋이 되기 때문에 입금 작업의 성공 여부와 상관없이 출금 작업만 별도 처리된다. 따라서 JDBC에서 트랜잭션을 코드로 제어하려면 자동 커밋 기능을 꺼야한다. 자동 커밋 설정 여부는 Connection의 setAutoCommit() 메소드로 할 수 있다.

다음은 자동 커밋 기능을 끈다. 자동 커밋 기능이 꺼지면, 아래와 같은 코드로 커밋과 롤백을 제어할 수 있다.

// 자동 커밋 끄기
con.setAutoCommit(false);

// 커밋하기
con.commit();

// 롤백하기
con.rollback();

 

트랜잭션을 처리한 이후에는 원래대로 자동 커밋 기능을 켜둬햐 한다. Connection을 다른 기능 처리를 위해 계속 사용해야 한다면 setAutoCommit(true) 코드로 자동 커밋 기능을 켜둬야 한다. 특히 커넥션 풀(Connection Pool)을 사용할 때 주의해야 할 부분이다.

[ 게시판 구현 ] ☆★

지금까지 학습한 JDBC를 활용해서 명령 프롬프트(윈도우) 또는 터미널(맥OS)에서 실행되는 게시판을 구현해보자. 게시판은 기본적인 CRUD(Create, Read, Update, Delete) 기능이 포함되어 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


[ 스트림이란? ]

import java.util.*;
import java.util.stream.Stream;

public class Test {
	public static void main(String[] args) {

		// p.723
		Set<String> set = new HashSet<String>();
		set.add("aa");
		set.add("bb");
		set.add("cc");
		
		Stream<String> stream = set.stream();
		
		// 람다식 구조
		stream.forEach(name->System.out.println(name)); //매개변수 이름은 name으로 준 것
		/* void b(String a) {
		 *   System.out.println(a); }
		   이렇게 적는 대신 람다식 구조로 적은 것 */
		
	}
}

 

 

728x90