본문 바로가기

Spring Boot/JPA

@Transactional(readOnly = false)일 때 얻는 이점

@TransactionalreadOnly param을 false로 설정하면 다음과 같은 이점을 얻을 수 있다.

  1. 변경감지를 위해 snapshot(객체의 초기상태)을 저장해야 하는데,
    이 동작을 하지 않아 메모리가 절약된다.
    이는 @QueryHint를 이용하여 readOnly로 설정했을 때와 동일한 효과이다.
    (Spring 5.1부터 적용된다)
  2. FlushModeMANUAL로 설정되어, 직접 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);
}

 

test 및 출력결과

loadedState는 변경감지를 위해 snapshot(객체의 초기상태)가 저장되는 공간으로,
readOnlytrue인 경우에는 null로 지정되어 아예 사용되지 않으며,

hibernateFlushMode를 확인해보면 MANUAL로 설정되는 것을 확인할 수 있는데,
해당 모드에서는 직접 flush하지 않으면 flush가 발생하지 않아 성능이점을 볼 수 있다.

 

 조금 혼란스러울 수 있는 이야기지만, readOnly option이 true로 되어있어도
saevdelete 등을 호출하고 강제로 flush를 하면
Transaction이 끝날 때 commit되면서 savedelete가 반영된다.
(H2 DB로 test해봤을 때는 정상작동했다. 다른 DB는 모르겠다.)

또한 공식문서에서도 readOnly option을 true로 하는 것이
반드시 쓰기시도를 막을 수 있는 것은 아니라고 써져있다.

물론 readOnlytrue로 해놓고 강제 flush하는
미친 코드를 마주하게 될 가능성은 극악이겠지만,
혹시 모른다... 알아두기는 해보자.

'Spring Boot > JPA' 카테고리의 다른 글

@Transactional method의 Transaction 전파  (0) 2022.02.08
Spring Boot + JPA 설정  (0) 2021.11.02