문서의 이전 판입니다!
@Transactional(readOnly=true)
는 JDBC의 Connection.setReadOnly(true)
를 호출한다. 이는 단지 “힌트”일 뿐이며 실제 데이터베이스 자체를 read-only 트랜잭션으로 생성할지 여부는 JDBC 드라이버에 따라 달라지고 행위도 데이터 계층 프레임워크의 구현여부에 따라 달라질 수 있다.
이 힌트로 정말로 read only 트랜잭션이 생성될 수도 있고, MySQL 리플리케이션 JDBC 드라이버의 경우 Write가 있을 경우에는 Master로 read only일 경우에는 Slave로 커넥션을 맺는 식의 선택을 하는 정도의 역할만 할 수도 있다.
Oracle JDBC 드라이버는 Read-Only 트랜잭션을 지원하며, MySQL의 경우 5.6.5 버전이 되어서야 이를 지원하게 되었다.
Spring-Hibernate 연동시에는 Session의 Flush Mode를 FLUSH_NEVER
로 변경한다. 그러나 데이터가 수정되면 수정된 상태로 세션의 1차 캐시에는 수정된 상태로 데이터가 남아있게 되면, 명시적으로 flush()
를 호출하면 쓰기 기능이 작동하게 된다.
단, 읽기 작업만 하더라도 트랜잭션을 걸어주는 것이 좋다. 트랜잭션을 걸지 않으면 모든 SELECT 쿼리마다 commit을 하기 때문에 성능이 떨어진다. 명시적으로 트랜잭션을 걸어주면 마지막에 명시적으로 commit을 해주면 되며, commit 횟수가 줄어서 성능이 좋아진다.
@Transactional
을 지정하지 않고 하나로 통일하고자 하는 경우에 괜찮을 듯.if (TransactionSynchronizationManager.isActualTransactionActive()) { TransactionStatus status = TransactionAspectSupport.currentTransactionStatus(); ... }
@Override @Transactional(readOnly = true) public Iterable<Book> findAll() { // true log.info("Outside : readOnly {}", TransactionSynchronizationManager.isCurrentTransactionReadOnly()); return new Iterable<Book>() { @Override public Iterator<Book> iterator() { // false log.info("In iterable : readOnly {}", TransactionSynchronizationManager.isCurrentTransactionReadOnly()); return findAllReal().iterator(); } }; } @Override @Transactional(readOnly = true) public List<Book> findAllReal() { // false log.info("Inside : readOnly {}", TransactionSynchronizationManager.isCurrentTransactionReadOnly()); return bookRepository.findAll(); }
// BookAnotherService @Autowired private BookService bookService; @Transactional(readOnly = true) public Iterable<Book> findAll() { // true log.info("Outside : readOnly {}", TransactionSynchronizationManager.isCurrentTransactionReadOnly()); return new Iterable<Book>() { @Override public Iterator<Book> iterator() { // false -> Transaction 영역이 아니므로 false가 맞음. log.info("In iterable : readOnly {}", TransactionSynchronizationManager.isCurrentTransactionReadOnly()); return bookService.findAllReal().iterator(); } }; } // BookService @Override @Transactional(readOnly = true) public List<Book> findAllReal() { // true log.info("Inside : readOnly {}", TransactionSynchronizationManager.isCurrentTransactionReadOnly()); return bookRepository.findAll(); }
PlatformTransactionManager#setDefaultTimeout
을 통해 기본 timeout 시간을 지정할 수 있다. 기본값은 -1
로 JDBC Driver 기본을 따르거나 무제한을 의미한다.@Transactional(timeout=“”)
을 통해서 각 단위별로 지정가능하다.