12. DB를 사용하면서 발생 가능한 문제점들
DB Connection과 Connection Pool, DataSource
JDBC 관련 API는 클래스가 아니라 인터페이스다. JDK의 API에 있는 java.sql 인터페이스를 각 DB 벤더에서 상황에 맞게 구현하도록 되어 있다. 같은 인터페이스라고 해도, 각 DB 벤더에 따라서 처리되는 속도나 내부 처리 방식은 상이하다.
Connection 객체를 생성하는 부분에서 발생하는 대기 시간을 줄이고, 네트워크의 부담을 줄이기 위해서 사용하는 것이 DB Connection Pool이다.
Statement와 PreparedStatement의 가장 큰 차이점은 캐시(cache) 사용 여부이다. Statement를 사용할 때와 PreparedStatement를 처음 사용할 때는 다음과 같은 프로세스를 거친다.
- 쿼리 문장 분석
- 컴파일
- 실행
Statement를 사용하면 매번 쿼리를 수행할 때마다 1~3 단계를 거치고, PreparedStatement는 처음 한 번만 세 단계를 거친 후 캐시에 담아서 재사용한다. 동일한 쿼리를 반복적으로 수행한다면 PreparedStatement가 DB에 훨씬 적은 부하를 주며, 성능도 좋다.
DB를 사용할 때 닫아야 하는 것들
ResultSet 객체가 닫히는 경우는 다음과 같다.
- close() 메서드를 호출하는 경우
- GC의 대상이 되어 GC되는 경우
- 관련된 Statement 객체의 close() 메서드가 호출되는 경우
GC가 되면 자동으로 닫히고, Statement 객체가 close되면 알아서 닫히지만, 0.00001초라도 빨리 닫으면 그만큼 해당 DB 서버의 부담이 적어지게 된다.
Conenction 객체는 다음 세 가지 경우에 닫히게 된다.
- close() 메서드를 호출하는 경우
- GC의 대상이 되어 GC되는 경우
- 치명적인 에러가 발생하는 경우
connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
...
} catch (Exception e) {
...
} finally {
try { rs.close(); } catch (Exception rse) {}
try { ps.close(); } catch (Exception pse) {}
try { conn.close(); } catch (Exception conne) {}
}
JDK 7에서 등장한 AutoClosable 인터페이스
try 블록이 시작될 때 소괄호 안에 close() 메서드를 호출하는 객체를 생성해 주면 간단하게 처리할 수 있다.
public String readFileNew(String fileName) throws IOException {
FileReader reader = new FileReader(new FIle(fileName));
try (BufferedReader br = new BufferedReader(reader)) {
return br.readLine();
}
}
별도로 finally 블록에서 close() 메서드를 호출할 필요가 없어졌다. 만약 close() 메서드 호출 대상이 여러 개라면 세미콜론으로 구분하여 try-with-resources 구문에 두 개 이상의 문장을 추가하면 된다.
JDBC를 사용하면서 유의할 만한 몇 가지 팁
- setAutoCommit() 메서드는 필요할 때만 사용하자. 여러 개의 쿼리를 동시에 작업할 때 성능에 영향을 주게 되므로 되도록 자제하자.
- 배치성 작업은 executeBatch() 메서드를 사용하자. 여러 개의 쿼리를 한 번에 수행할 수 있기 때문에 JDBC 호출 횟수가 감소되어 성능이 좋아진다.
- setFetchSize() 메서드를 사용하여 데이터를 더 빠르게 가져오자.
- 한 건만 필요할 때는 한 건만 가져오자.