[JAVA]JDBC

신동혁·2022년 8월 12일
0

JAVA

목록 보기
13/16

1.JDBC란?

JDBC(Java Database Connectivity)는 자바에서 데이터베이스에 접속할 수 있도록 제공하는 API다. 자바에서 모든 DBMS를 사용할 수 있도록 인터페이스와 클래스로 구성된 JDBC를 만들었다. 대부분 인터페이스로 구성되어 있으며 클래스는 단 하나 존재하고 이 클래스가 바로 DriverManager클래스다. 이렇게 JDBC가 주로 인터페이스로 이루어진 이유는 벤더(DBMS회사)별로 sql문법이 다르기 때문이다. DB별 구현클래스를 따로 만들게 된다면 자바에서 해당 DB들을 사용할 때마다 DB별로 구현클래스를 따로 import하고 또 해당 구현클래스에 맞는 동작방식에 맞춰 다르게 사용해야 한다. 이런 불편함을 해결하기 위해 자바는 하나의 인터페이스만 만들고 벤더들에게 만들어진 JDBC 인터페이스에 맞게 구현을 맡겼다. 이 경우 각 벤더들이 만든 DB들의 구현클래스들이 달라도 상위 인터페이스가 모두 JDBC로 동일하므로 모든 구현클래스는 동일한 동작방식을 공유하게 된다. 이렇게 각각의 벤더들이 만든 구현 클래스를 JDBC 드라이버라고 하며 드라이버는 벤더사의 DBMS별로 다르다.

자바 JDBC는 java.sql 패키지로 제공된다.

2.JDBC 실행구조

JDBC실행구조도

3.JDBC 실행순서

  1. .properties파일 생성 및 설정
  2. JDBC Driver 로드
  3. Connection객체 생성
  4. Statement/PreparedStatement객체 생성
  5. Query 수행
  6. int 처리 / ResultSet 처리
  7. 연결 해제

3.1 .properties파일 생성 및 설정

.properties파일은 DB의 driver,url,id,pw값을 가지고 있는 파일이다.

properties 확장자명 파일 생성 이유는 다음과 같다. 만약 mysql(DB의 한 종류)을 이용하는 코드가 있다고 가정한다. 이때 사용하는 DB를 oracle로 바꾼다면 코드상에서 바꿔줘야 하는 코드들이 많다. 예를 들어 Connection객체를 만드는 코드는 다음과 같다.

Connection 커넥션객체명 = DriverManager.getConnection(jdbc의url값, jdbc계정id, jdbc계정pw);

이 코드에서 파라미터값으로 받는 값들을 모두 mysql의 url,id,pw값에서 oracle url,id,pw값으로 수정해줘야 한다. 이는 굉장히 번거로울 수 있으며, 만약 자바 코드가 웹상에서 실행되는 상황이다? 그러면 모든 작업을 중지시키고 코드를 수정한 뒤 다시 웹상에 올리는 과정이 필요하다. 이런 문제점을 해결하기 위해 .properties파일을 사용한다. DB에 대한 driver,url,id,pw값을 가지고 있는 .properties파일을 만들고 이 파일에서 원하는 값을 가져가도록 코드를 작성한다면, DB를 변경하거나 사용할 계정을 바꾸는 등의 작업을 할 때 코드를 직접 수정할 필요없이 단순히 .properties파일에 존재하는 DB에 대한 driver,url,id,pw값들만 수정해 해결할 수 있다. 다음은 mysql에 대한 DB정보를 작성한 .properties파일의 예시다.

# mysql
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:url주소
jdbc.id=사용할계정아이디
jdbc.pw=사용할계정비밀번호

위 파일을 'dbinfo.properties'라는 이름으로 저장했다고 가정한다. 그리고 코드 상에서 위 파일을 불러와 DB에 대한 driver정보를 받아놓는 클래스를 간단히 구현해본다.

3.2 JDBC Driver 로드

DButil클래스를 생성해 클래스가 메모리에 올라가면 자동으로 'dbinfo.properties'를 읽고 DB에 대한 드라이버위치를 불러와 드라이버를 로드해놓도록 한다.

public class DButil {
	static Properties dbinfo = new Properties(); // DBuitl클래스가 메모리에 올라가면 자동으로 한번 Properties 객체를 생성한다.
	static { // DButil클래스가 메모리에 올라가면 자동으로 한번 "dbinfo.properties"파일을 읽고,
            // jdbc.driver에 해당하는 값을 
		try {
			dbinfo.load(new FileReader("dbinfo.properties"));  // 'dbinfo.properties'읽기 !!!
			Class.forName(dbinfo.getProperty("jdbc.driver"));  //  DB에 대한 드라이버위치 불러와 로드 !!!
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("해당 mysql db의 driver가 없습니다");
		} catch (IOException e) {
			e.printStackTrace();
			;
			System.out.println("properties 파일이 없습니다");
		}
	}

	public static Connection getConnection() throws SQLException {
		return DriverManager.getConnection(
				dbinfo.getProperty("jdbc.url"),
				dbinfo.getProperty("jdbc.id"),
				dbinfo.getProperty("jdbc.pw"));
	}
}

3.3 Connection객체 생성

Connection객체는 데이터베이스와의 연결 정보를 담고 있는 객체다.

public class DButil {
	static Properties dbinfo = new Properties();
	static { 

		try {
			dbinfo.load(new FileReader("dbinfo.properties")); 
			Class.forName(dbinfo.getProperty("jdbc.driver"));  
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("해당 mysql db의 driver가 없습니다");
		} catch (IOException e) {
			e.printStackTrace();
			;
			System.out.println("properties 파일이 없습니다");
		}
	}

	public static Connection getConnection() throws SQLException { // Connection객체 생성 !!!
		return DriverManager.getConnection( // JDBC의 유일한 클래스인 DriverManager의 getConnection메서드를 이용
				dbinfo.getProperty("jdbc.url"), // getConnection 파라미터값으로 'dbinfo.properties'에 저장된 url,id,pw값을 불러온다
				dbinfo.getProperty("jdbc.id"),
				dbinfo.getProperty("jdbc.pw"));
	}
}
Connection conn = DButil.getConnection(); // conn이라는 이름의 Connection객체를 DButil클래스의 getConnection메서드를 통해 생성한다.

Connection객체 생성까지는 DButil클래스에서 static블록을 이용해 DButil클래스가 메모리에 올라가면 한번만 자동으로 실행되도록 했다. 즉, getConnection메서드가 호출되어 DButil클래스가 메모리에 올라가면 더 이상의 Driver 로드나 Connection객체 생성은 필요없다.

3.4 Statement/PreparedStatement객체 생성

이제 DB와 연결된 정보를 담고 있는 Connection객체를 이용해 Statement/PreparedStatement객체를 생성한다. (원하는 인터페이스 선택) 이 객체는 sql 쿼리문을 담을 객체다.

Statement stmt = conn.createStatement("insert into 테이블명 values (9,8,7)");
// stmt라는 이름의 Statement객체를 conn이라는 이름의 Connection객체의 createStatement메서드를 통해 생성한다.
// createStatement파리미터로는 sql 쿼리문을 넣는다.
PreparedStatement pstmt = conn.prepareStatement("insert into 테이블명 values (?, ?, ?)");
pstmt.set타입(1,넣을값);
pstmt.set타입(2,넣을값);
pstmt.set타입(3,넣을값);
// pstmt라는 이름의 PreparedStatement객체를 conn이라는 이름의 Connection객체의 createStatement메서드를 통해 생성한다.
// prepareStatement파라미터로는 sql 쿼리문을 넣는다.

두 인터페이스의 차이점은 컴파일하는 방식이다. statement는 쿼리문을 실행할 때마다 컴파일을 하지만, preparedstatement는 쿼리문을 처음으로 실행할 때만 프리컴파일을 해놓고 이후 '?'로 표현된 부분에 대한 값만 받아가며 쿼리를 실행한다. 즉 같은 insert쿼리문을 100번 반복할 때, statement는 100번의 컴파일을 하지만, preparedstatement는 1번만 프리컴파일하고 100번의 변수대입을 해 효율을 높인다. 이렇게 반복적인 sql작업에 최적화된 것이 preparedstatement이다.

3.5 Query 수행

PreparedStatement객체 pstmt를 이용하는 경우로 설명하겠다.

  • executeUpdate() : sql 쿼리문이 update, delete, insert인 경우 사용하는 메서드, 반화타입은 int( int값은 해당 sql 쿼리문이 실행된 횟수를 의미한다 )
int count = pstmt.executeUpdate();
  • executeQuery() : sql 쿼리문이 select인 경우 사용하는 메서드, 반환타입은 ResultSet( ResultSet객체는 select구문실행결과를 담은 객체다 )
Resultset rset = pstmt.executeQuery();

3.6 int 처리 / ResultSet 처리

  • executeUpdate메서드로 update, delete, insert쿼리문을 실행했을 때
// 실행 횟수를 담은 count를 이용해 원하는 작업이 가능하다.
// 다음은 sql문이 한 번 실행되면 "sql 쿼리문 1번 실행됨"문자열을 출력하는 코드다.
if (count == 1) { 
	System.out.println("sql 쿼리문 1번 실행됨"); 
}
  • executeQuery메서드로 select쿼리문을 실행했을 때
// ResultSet객체를 순회하며 원하는 작업이 가능하다.
// 다음은 ResultSet객체 rset의 모든 행에 걸쳐 필드1에 해당하는 값, 필드2에 해당하는 값을 출력하는 코드다. 
while(rset.next()){
    System.out.println(rset.get필드1타입("필드1") + rset.get필드2타입("필드2"));
    // 이때 필드명 대신 rset 필드 순서대로 1,2,3.. 이런식으로 표현해서 'rset.get필드1타입(1) + rset.get필드2타입(2)'도 가능하다.
}

여기서, next메서드는 cursor를 다음 row로 이동시킨다. while문의 조건으로 next메서드를 이용해 '다음 커서에 행이 존재하면 진행하고, 다음 커서에 행이 존재하지 않으면 break해라'라는 코드를 작성해 ResultSet객체 rset에 담긴 모든 행을 순회할 수 있는 것이다. 이 밖에 ResultSet객체에 사용할 수 있는 메서드를 소개한다.

  • ResultSet.next() : Cursor를 다음 Row로 이동
  • ResultSet.previous() : Cursor를 이전 Row로 이동
  • ResultSet.first() : Cursor를 ResulSet의 처음으로 이동
  • ResultSet.last() : Cursor를 ResulSet의 마지막으로 이동
  • ResultSet.isFirst() : 현재 Cursor가 첫 Row인지를 확인
  • ResultSet.isLast() : 현재 Cursor가 마지막 Row인지를 확인
  • ResultSet.getType() : ResultSet의 Type을 Return
  • ResultSet.getCucurrency() : Concurrency의 Type을 Return
  • ResultSet.getRow() : 현재 Cursor가 가리키고 있는 Row Number

3.7 연결 해제

만약 생성된 객체들을 닫지 않을 경우 계속 사용하는 상태로 인식을 해 자바의 가비지콜렉터는 해당 객체들을 삭제하지 않는다. 그러므로 사용하지 않는 객체들에 대한 close는 자원 관리 측면에서 굉장히 중요하다.

rset.close(); // ResultSet객체 rset 반환
pstmt.close(); // PreparedStatement객체 pstmt 반환
conn.close(); // Connection객체 conn 반환
profile
개발취준생

0개의 댓글