@Transactional의 readOnly param을 false로 설정하면 다음과 같은 이점을 얻을 수 있다.
- 변경감지를 위해 snapshot(객체의 초기상태)을 저장해야 하는데,
이 동작을 하지 않아 메모리가 절약된다.
이는 @QueryHint를 이용하여 readOnly로 설정했을 때와 동일한 효과이다.
(Spring 5.1부터 적용된다) - FlushMode가 MANUAL로 설정되어, 직접 flush하지 않으면
flush가 일어나지 않게 된다.
(Transaction commit이나 JPQL 실행 전과 같이
flush가 발생해야 하는 상황에서 발생하지 않는다.)
(이 기능은 Hibernate의 전용기능이다.)
@Test
@Transactional(readOnly = true)
public void readOnlyTransactionalTest() {
Member member = memberJpaRepository.findById(1L).orElse(null);
SharedSessionContractImplementor session = em.unwrap(SharedSessionContractImplementor.class);
org.hibernate.engine.spi.PersistenceContext cont = session.getPersistenceContext();
EntityEntry entry = cont.getEntry(member);
System.out.println("Snapshot: " + entry.getLoadedState());
System.out.println("FlushMode: " + session.getHibernateFlushMode());
assertThat(entry.getLoadedState()).isNull();
assertThat(session.getHibernateFlushMode()).isEqualTo(FlushMode.MANUAL);
assertThat(session.getFlushMode()).isEqualTo(FlushModeType.COMMIT);
}
loadedState는 변경감지를 위해 snapshot(객체의 초기상태)가 저장되는 공간으로,
readOnly가 true인 경우에는 null로 지정되어 아예 사용되지 않으며,
hibernateFlushMode를 확인해보면 MANUAL로 설정되는 것을 확인할 수 있는데,
해당 모드에서는 직접 flush하지 않으면 flush가 발생하지 않아 성능이점을 볼 수 있다.
조금 혼란스러울 수 있는 이야기지만, readOnly option이 true로 되어있어도
saev나 delete 등을 호출하고 강제로 flush를 하면
Transaction이 끝날 때 commit되면서 save와 delete가 반영된다.
(H2 DB로 test해봤을 때는 정상작동했다. 다른 DB는 모르겠다.)
또한 공식문서에서도 readOnly option을 true로 하는 것이
반드시 쓰기시도를 막을 수 있는 것은 아니라고 써져있다.
물론 readOnly를 true로 해놓고 강제 flush하는
미친 코드를 마주하게 될 가능성은 극악이겠지만,
혹시 모른다... 알아두기는 해보자.
'Spring Boot > JPA' 카테고리의 다른 글
@Transactional method의 Transaction 전파 (0) | 2022.02.08 |
---|---|
Spring Boot + JPA 설정 (0) | 2021.11.02 |