JDBC는 Java DataBase Connectivity의 약자로서 Java 진영에서 RDBMS와 연결하기 위한 API입니다.
가장 많이 쓰이는 MyBatis, JPA 모두 기저 기술로 JDBC를 이용하기 때문에 Java로 서버 개발을 한다면 JDBC는 필수라고 해도 과언이 아닙니다.
오늘은 JDBC가 DB와 어떻게 연결을 맺는 지 소스코드로 간단하게 살펴보겠습니다.
JDBC 라이브러리는 java.sql에 위치합니다.
본격적으로 JDBC의 커넥션을 살펴보기 전에 JDBC가 필요한 이유에 대해 알아봅시다.
현재 RDBMS의 종류는 Oracle부터 시작해서 MySQL, MariaDB 등등 많이 존재합니다.
RDBMS 종류마다 DB와 연결하는 방법, 쿼리를 날리고 결과를 받아오는 방법들이 다 다릅니다.
따라서, DB를 바꾸게 된다면 애플리케이션의 코드도 바꾸어야하는 불상사가 생기게 됩니다.
이를 방지하기 위해 Java 진영에서 RDBMS 통신을 표준화하였고 이를 JDBC라고 합니다.
JDBC가 DB와 연결을 위해 이용하는 첫번째 방법은 Driver Manager를 이용하는 것입니다.
Driver Manager는 각 DB마다 연결을 담당하는 Driver
를 관리하는 클래스입니다.
Driver는 JDBC가 제공하는 인터페이스로, 각 DB마다 Driver 인터페이스를 구현하는 클래스를 제공해야 한다. 인터페이스의 주요 메소드로
connect
,acceptsURL
이 있다.
java.sql에 있는 DriverManager
코드를 간략하게 살펴보면, 다음과 같습니다.
public class DriverManager {
...
public static Connection getConnection(String url,
String user, String password) throws SQLException { ... }
public static void registerDriver(java.sql.Driver driver,
DriverAction da) throws SQLException { ... }
public static void deregisterDriver(Driver driver) throws SQLException { ... }
public static Driver getDriver(String url) throws SQLException { ... }
public static Enumeration<Driver> getDrivers() { ... }
public static Stream<Driver> drivers() { ... }
...
}
registerDriver
메소드를 통해 각 DB 드라이버를 등록하며, deregisterDriver
메소드를 통해 등록된 Driver를 해제하게 됩니다.
드라이버를 등록할 때 사용되는 2번째 파라미터 DriverAction
은 해당 드라이버가 등록해제 될 때 DriverManager
로 부터 알림을 받기 위해 정의하는 인터페이스입니다.
getDriver
는 해당 URL을 해석할 수 있는 드라이버를 찾아줍니다. 각 드라이버들은 URL의 sub-protocol을 보고 판단하게 됩니다.
예를 들어, MySQL의 DB URL은 jdbc:mysql://ip:3306/db
의 형태를 띄고 있는데, MySQL 드라이버는 jdbc:
다음 부분에 mysql
으로 해당 요청이 MySQL을 위한 요청임을 알게 됩니다.
getDriver
, drivers
메소드를 이용하면 등록된 Driver를 각각 Enumeration, Stream의 형태로 받아볼 수 있습니다. 내부적으로 해당 드라이버가 ClassLoader로 로드되어 있는 지 확인하게 됩니다.
제일 중요한 getConnection
메소드는 실제 연결을 할 때 사용하게 됩니다.
입력 파라미터로 연결할 DB의 url, user, password를 제공하면 Connection
객체가 반환됩니다.
이 외에도, 로그 기록을 설정하거나 로그인 타임아웃을 설정하는 메소드가 존재합니다.
생각보다 단순하게 동작합니다.
등록된 모든 드라이버들에게 connect
메소드를 통해 연결을 요청하고, 연결에 성공하게 되면 바로 Connection
객체를 반환합니다.
Driver Manager를 직접 이용하는 방법 말고도 JDBC는 DataSource
인테페이스를 제공합니다.
HikariCP와 같이 커넥션 풀을 이용하는 방식을 지원하기 위해 추상화된 인테페이스가 있는 것입니다.
주의: DataSource 인터페이스 자체는 커넥션 풀임을 보장하지 않습니다. DataSource를 구현하는 객체 중에 커넥션 풀이 있을 뿐입니다.
커넥션 풀이란?
일반적으로 데이터베이스와 커넥션을 맺기 위해서 TCP/IP 요청을 하고 DB 연결을 위한 정보를 넘겨주어야 한다. 데이터베이스와 통신할 때마다 생기는 이 오버헤드를 줄이기 위해서 미리 커넥션을 여러개를 맺어놓고 하나의 Pool로 관리하는 방식을 말한다. 데이터베이스와 통신할 때 이 Pool에서 커넥션을 꺼내와서 사용하고, 사용 후에 반납하는 방식으로 관리가 이루어진다.
DataSource
는 DriverManager
와 다르게 인터페이스의 형태로 제공됩니다.
Datasource 인터페이스는 DriverManager와 유사하게 설계되어 있습니다.
public interface DataSource extends CommonDataSource, Wrapper {
...
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password) throws SQLException;
...
}
MySQLDataSource는 MySQL Connector J 라이브러리(Github)에 있는 MySQL을 위한 DataSource이다.
내부적으로 NonRegisteringDriver
객체를 두어서 getConnection()
이 호출될 때, 해당 객체의 connect()
메소드를 호출해서 커넥션을 맺게 된다.
MySQL에서 Driver를 제공할 때, NonRegisteringDriver
를 Wrapping Class로 감싸서 제공한다.
커넥션 풀을 이용하는 DataSource로 HikariCP(Github)가 대표적이다.
HikariDataSource도 마찬가지로 DataSource 인터페이스를 구현한 클래스인데, 내부적으로 HikariPool
객체를 통해 커넥션을 맺는다.
재밌는 점은, 커넥션 풀의 Lazy Initialization으로 인하여 생성 방식에 따라 첫 getConnection()
호출의 퍼포먼스가 달라질 수 있다.
JDBC는 DB 연결을 위해 DriverManager
클래스를 제공하기도, DataSource
인터페이스를 제공합니다.
현재는 DataSource
인터페이스를 이용하는 방식이 권장된다고 합니다. 이와 관련된 StackOverflow 답변을 읽어보시는 것을 추천드립니다.
Spring에서는 DataSource
를 구현한 DriverManagerDataSource
클래스를 제공하니, 이 부분도 참고하시면 좋을 것 같습니다.