스프링이 제공하는 'PlatformTransactionManager'는 크게 두가지 역할을 한다. 첫번째는 트랜잭션을 추상화하는 것이고 그 다음으로는 리소스를 동기화하는 것이다.
트랜잭션을 유지하려면 트랜잭션이 시작될 때부터 끝까지 같은 데이터베이스의 커넥션을 유지해야 한다. 하지만 파라미터로 매번 커넥션을 전달하는 방식을 사용하면 코드가 지저분해지고 유지보수가 힘들어지는 등 여러가지 단점이 많다. 그렇다면 어떤 방법으로 트랜잭션을 일관되게 만들 수 있을까?
스프링은 트랜잭션 동기화 매니저를 제공하는데 이 동기화 매니저는 쓰레드 로컬(ThreadLocal)을 사용해서 커넥션을 안전하게 보관하고 동기화해주는 역할을 한다. 따라서 동기화 매니저를 사용하면 멀티쓰레드 환경에서도 안전하게 커넥션을 동기화할 수 있고 커넥션이 필요할때는 파라미터로 커넥션을 전달하지 않아도 동기화 매니저를 통해 획득할 수 있다.
트랜잭션 동기화 기능을 사용하기 위해 스프링이 제공하는 DataSourceUtils를 사용한다.
private Connection getConnection() throws SQLException {
//스프링이 제공하는 DataSourceUtils
//DataSourceUtils로 트랜잭션 동기화 사용 가능
Connection con = DataSourceUtils.getConnection(dataSource);
log.info("get connection={} class={}", con, con.getClass());
return con;
}
DataSourceUtils.getConnection을 따라가서 자세히 살펴보면 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내오는 것을 확인할 수 있다.
커넥션을 닫을때도 DataSourceUtils의 releaseConnection을 사용한다.
private void close(Connection con, Statement stmt, ResultSet rs) {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, dataSource);
// JdbcUtils.closeConnection(con);
}
DataSourceUtils.getConnection으로 커넥션을 호출하면 트랜잭션 동기화 매니저는 보관하고 있는 커넥션이 있는 경우에는 해당 커넥션을 반환하고 없다면 새로운 커넥션을 생성해서 반환한다.
커넥션을 close해서 직접 닫아버리면 커넥션이 유지되지 않는 문제가 발생한다. 커넥션은 트랜잭션이 종료(커밋 or 롤백) 될 때까지 살아있어야 하기 때문이다. 이때 DataSourceUtils.releaseConnection을 사용하면 커넥션을 바로 닫지 않고 동기화된 커넥션은 그대로 유지해준다. 하지만 트랜잭션 동기화 매니저가 관리하는 커넥션이 없는 경우에는 해당 커넥션을 닫는다.
이 글은 김영한님의 스프링 DB 1편 - 데이터 접근 핵심 원리 강의를 듣고 정리한 내용입니다.