

커넥션을 얻는 방법은 JDBC DriverManager를 직접 사용하거나, 커넥션 풀을 사용하는 등 다양한 방법이 존재한다. DriverManager를 통해서 커넥션을 획득하다가, 커넥션 풀을 사용하는 방법으로 변경하려며 어떻게 해야할까?
-> 커넥션을 획득하는 방법을 추상화하면 된다.

javax.sql.DataSource라는 인터페이스를 제공한다.public interface DataSource {
Connection getConnection() throws SQLException;
}
DataSource 정리
대부분의 커넥션 풀은 DataSource 인터페이스를 이미 구현해두었다. 따라서 DBCP2 커넥션 풀, HikariCP 커넥션 풀의 코드를 직접 의존하는 것이 아니라 DataSource 인터페이스에만 의존하도록 작성하면 된다.
DriverManager는 DataSource 인터페이스를 사용하지 않는다. DriverManager는 직접 사용해야 하지만 스프링은 DriverManager도 DataSource를 통해서 사
용할 수 있도록 DriverManagerDataSource라는 DataSource를 구현한 클래스를 제공한다.
먼저 기존에 개발했던 DriverManager를 통해서 커넥션을 획득하는 방법을 확인해보자.
@Test
void driverManager() throws SQLException {
Connection con1 = DriverManager.getConnection(URL, USERNAME, PASSWORD);
Connection con2 = DriverManager.getConnection(URL, USERNAME, PASSWORD);
log.info("connection={}, class={}", con1, con1.getClass());
log.info("connection={}, class={}", con2, con2.getClass());
}

각자 다른 2개의 connection을 획득했다. 이번에는 스프링이 제공하는 DataSource가 적용된 DriverManager인 DriverManagerDataSource를 사용해보자.
@Test
void dataSourceDriverManager() throws SQLException {
// DriverManagerDataSource - 항상 새로운 커넥션 획득
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
useDataSource(dataSource);
}
private void useDataSource(DataSource dataSource) throws SQLException {
Connection con1 = dataSource.getConnection();
Connection con2 = dataSource.getConnection();
log.info("connection={}, class={}", con1, con1.getClass());
log.info("connection={}, class={}", con2, con2.getClass());
}

DriverManagerDataSource는 DataSource를 통해서 커넥션을 획득할 수 있다.이번에는 DataSource를 통해 커넥션 풀을 사용해보자.
@Test
void dataSourceConnectionPool() throws SQLException, InterruptedException {
//커넥션 풀링: HikariProxyConnection(Proxy) -> JdbcConnection(Target)
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
dataSource.setMaximumPoolSize(10); //커넥션 풀 최대 사이즈
dataSource.setPoolName("MyPool"); //풀의 이름
useDataSource(dataSource);
Thread.sleep(1000);
}
HikariCP 커넥션 풀을 사용한다. HikariDataSource는 DataSource 인터페이스를 구현하고 있다.Thread.sleep을 통해 대기 시간을 주었다. 코드를 실행시키고 로그를 확인해보자.@Slf4j
public class MemberRepositoryV1 {
private final DataSource dataSource;
//의존관계 주입
public MemberRepositoryV1(DataSource dataSource) {
this.dataSource = dataSource;
}
//save()...
//findById()...
//update()....
//delete()....
//JdbcUtils 사용
private void close(Connection con, Statement stmt, ResultSet rs) {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(stmt);
JdbcUtils.closeConnection(con);
}
//DataSource 사용
private Connection getConnection() throws SQLException {
Connection con = dataSource.getConnection();
log.info("get connection={}, class={}", con, con.getClass());
return con;
}
}
DataSource : 표준 인터페이스이기 때문에 DriverManagerDataSource에서 HikariDataSource로 변경되어도 해당 코드를 변경하지 않아도 됨, 대신 DataSource 의존관계 주입이 필요
JdbcUtils : JDBC를 편리하게 다룰 수 있는 편의 메서드 제공, 커넥션을 좀 더 편리하게 닫을 수 있음
@Slf4j
class MemberRepositoryV1Test {
MemberRepositoryV1 repository;
@BeforeEach
void beforeEach() throws Exception {
//기본 DriverManager - 항상 새로운 커넥션 획득
//DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
//커넥션 풀링: HikariProxyConnection -> JdbcConnection
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
//의존성 주입
repository = new MemberRepositoryV1(dataSource);
}
@Test
void crud() throws SQLException {
//save()...
//findById()...
//update()....
//delete()....
}
}

-> DI+OCP, 이것이 DataSoure를 사용하는 장점이다.