문제를 설명하기 전에 몇 가지 전제를 설명하겠습니다. 데이터베이스 트랜잭션이 다음과 같은 방식으로 Spring 구성 파일에 구성되어 있다고 가정합니다.
<bean id="transactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation 기반 transaction-manager="transactionManager" />
이제 UserDao와 SecurityService가 있습니다:
@Repository public class UserDao { public User getUser() { // 사용자 테이블에서 사용자 쿼리 return queryObject("select * from user order by id desclimit 1");
@Service @Transactional public class SecurityService { @Autowired private UserDao userDao; public void checkUserInfo() { while(true) { User user = userDao.getUser(); if(user != null && "Tom".equals(user.getName ()) { System.out.println("톰이 여기 있습니다") } } } }
SecurityService#checkUserInfo() 메소드를 호출하는 과정에서 userDao#getUser() 메소드를 통해 얻은 데이터는 변경되지 않고 이때 Tom이라는 새로운 데이터가 삽입되더라도 루프가 종료되지 않습니다. 또한 SecurityService에서 @Transactional 주석을 제거해도 도움이 되지 않습니다.
우선 데이터베이스 커넥션 풀에 문제가 있지 않을까 생각했는데, Spring 자체의 커넥션 풀로 교체했을 때도 마찬가지다. 그러다가 JdbcTemplate에서 직접 Connection 객체를 호출하고 원래의 JDBC 메소드를 사용하여 데이터베이스를 운용했다. 이때 데이터가 실시간으로 바뀌기 때문에 Spring의 트랜잭션이 현재 작업 스레드에 바인딩되어야 한다고 생각했습니다. 소스 코드를 확인한 후 DataSourceUtils#doGetConnection 메소드에서 Spring이 각 스레드의 각 DataSource에 Connection을 생성하고 이를 트랜잭션에 바인딩한 것을 발견했습니다. tx:annotation 기반 구성 파일은 모든 Service 계층(@Service로 주석이 붙은 클래스)에 대해 트랜잭션 바인딩을 수행하므로 @Transactional 사용 여부에 관계없이 동일한 Connection이 동일한 스레드에 바인딩되지만 비즈니스 작업만 수행되는 것은 아닙니다. .
많은 실험과 정보 검색 끝에 마침내 완벽한 해결책을 찾았습니다. 위의 checkUserInfo 메서드에 @Transactional(propagation = Propagation.NOT_SUPPORTED) 주석을 추가하기만 하면 됩니다. 물론, Connection을 얻어 수동으로 작업을 수행하거나 DateUtils 패키지를 사용하여 작업을 수행할 수도 있습니다.