JDBC(Java DataBase Connectivity)는 데이터베이스에 접속할 수 있도록 해주는 자바 API다.
중요한 데이터를 데이터베이스에 저장하기 위해 애플리케이션 서버가 데이터베이스를 사용하는 방식은 다음과 같다.
하지만 수십개가 넘는 각각의 DB마다 커넥션 연결, SQL 전달, 결과를 응답받는 방식이 모두 다르기 때문에 크게 2가지 문제가 발생한다.
이 문제를 해결하기 위해서 자바에서 JDBC 표준 인터페이스를 만들었고, 각각의 DB 벤더에서 자신의 DB에 맞게 JDBC 인터페이스를 구현해서 라이브러리로 제공하기 시작했다. 여기서 제공되는 라이브러리를 JDBC 드라이버라고 한다.
String sql = "select * from member where member_id = ?";
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
JDBC를 사용하기 전에 SQL문을 정의해놓고, Connection
, PreparedStatement
, ResultSet
을 선언한다.
(try-catch문 안에서 커넥션을 연결해야 하기 때문에 미리 선언해서 null을 대입해준다.)
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, memberId);
rs = pstmt.executeQuery();
if (rs.next()) {
Member member = new Member();
member.setMemberId(rs.getString("member_id"));
member.setMoney(rs.getInt("money"));
return member;
} else {
throw new NoSuchElementException("member not found memberId = " + memberId);
}
} catch (SQLException e) {
log.error("error", e);
throw e;
} finally {
close(con, pstmt, rs);
}
getConnection()
을 통해 커넥션을 획득하고, 정의해놓은 SQL문을 DB에 전달한다.
전달한 SQL문에 대한 응답인 ResultSet을 받아와서 Member 객체를 생성 후 반환한다.
여기서 Connection
, PrepareStatement
, ResultSet
모두 사용 후 종료해야 하기 때문에 finally 부분에서 close()
를 작성한다.
커넥션을 종료하는 close() 메서드는 다음과 같이 작성되어 있다.
private void close(Connection con, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
log.error("error", e);
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
log.error("error", e);
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
log.error("error", e);
}
}
}
위에서 보듯 데이터베이스 커넥션을 획득하는 과정은 꽤 복잡하고, 시간도 오래 걸린다. 특히 사용자가 애플리케이션을 사용할 때 SQL 실행 시간뿐만 아니라, 커넥션을 새로 생성하는 시간도 추가되어 응답 속도가 느려진다.
이러한 문제를 해결하고자 커넥션 풀이 등장하게 되었다.
커넥션 풀(Connection Pool)은 말 그대로 커넥션을 관리하는 풀로, 다음과 같은 방식으로 동작한다.
커넥션 풀 덕분에 애플리케이션 로직에서 원할 때마다 JDBC 드라이버가 아닌 커넥션 풀에서 커넥션을 획득할 수 있다.
여기서 주의할 점은 커넥션을 사용하고나서 종료하는 것이 아니라, 커넥션 풀에 다시 반환한다는 것이다.
커넥션을 얻기 위해서 JDBC DriverManager를 사용하거나 커넥션 풀을 사용하는 등 다양한 방법이 존재한다.
만약 커넥션 획득 방법을 변경하려 한다면, 코드 자체도 변경해야 한다는 불편함이 존재한다. 이를 해결하고자 커넥션 획득 방법을 DataSource
라는 인터페이스로 추상화한 것이다.
// 기본 DriverManager - 항상 새로운 커넥션을 획득
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
// 커넥션 풀링
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
// dataSource 주입
repository = new MemberRepositoryV1(dataSource);
DataSource를 만들고 URL, USERNAME, PASSWORD를 입력하는 부분인 설정과 DataSource
의 getConnection()
을 호출하는 부분인 사용을 분리함으로써 향후 변경에 더 유연하게 대처할 수 있다.