본문 바로가기
💻 백엔드 개발/JPA - Hibernate

02. 당신의 @Transactional의 readOnly 옵션 잘 쓰고 있나요?

by YangsDev 2021. 2. 4.

시작하며

오늘은 Spring에서 마법의 어노테이션이라고 부르는 @Transactional의 readOnly 옵션에 대하여 이야기해보려고 한다.

 

엥? @Transactional에는 readOnly라는 옵션이 없는데?

엥?  @Transactional에는 readOnly 옵션이 없는데 이게 무슨일이죠? 없는 걸로 이야기를 하려고 하는 건가?

우리는 이번 readOnly 이야기를 하기 전에 @Transactional은 2가지 있다는 것을 이야기하고 넘어가려고 한다.

서로 다른 패키지를 가진 2개의 어노테이션

이 2가지가 궁극적으로 하려고 하는 일은 동일하다.  (실제 Spring AOP 상에서는  javax 패키지의 트랜젝션과 Spring 패키지의 트랜젝션도 모두 지원하고 있다.)  Spring 패키지를 통해 제공되는 어노테이션이 더 많은 옵션을 제공하게 된다..

 

이 글에서는 readOnly가 하는 역할에 대해 이야기를  하려고 하니, 자세한 내용은 구글에서 찾아보도록 하자 (더 이야기하면 너무 삼천포로 많이 빠질 것 같아서..)

 

 

@Transactional의 readOnly는 어떻게 동작할까?

@Transactional의 readOnly가 어떻게 동작하는지 탐험을 떠나 봅시다

일단 readOnly 옵션에 대해 짚고 넘어 가보도록 하자

- 트랜젝션을 읽기 전용으로 설정 가능함.

- 개발자의 실수로 해당 트랜젝션 안에서 Write가 발생하는 것을 방지될 수 도 있고, 안될 수도 있다.  (JDBC Driver에 따라 다르다.)

 

먼저, Spring 트랜젝션의 readOnly 옵션에 대한 API 문서를 먼저 확인해보도록 하자.

 

Transactional (Spring Framework 5.3.3 API)

Defines zero (0) or more exception names (for exceptions which must be a subclass of Throwable), indicating which exception types must cause a transaction rollback. This can be a substring of a fully qualified class name, with no wildcard support at presen

docs.spring.io

This just serves as a hint for the actual transaction subsystem; it will not necessarily cause failure of write access attempts. A transaction manager which cannot interpret the read-only hint will not throw an exception when asked for a read-only transaction but rather silently ignore the hint.

공식문서에서 해당 옵션은 이 옵션이 읽기 전용 힌트를 제공한다.라고 이야기를 한다.

이는 기본적으로 'Connection.setReadOnly(true)'를 호출하게 된다. 위에서 '될 수 도있고, 안 될 수도 있다'라고 이야기 한 이유도 이러한 이유다. 

Oracle Driver의 경우는 예전부터 readOnly 옵션을 제공하였지만, MySQL Driver는 5.6.5 이상이 되어야 드디어 지원되었다고 한다.

그 외에 H2에서는 아직 지원을 하지 않는다고도 하고, JDBC 제조사 별로 다르게 적용된다는 점을 꼭 인지 해야 한다.

(관련된 참고 글 : wonwoo.ml/index.php/post/839)

 

Hibernate로 사용할 때는 어떻게 적용될까?

Hibernate는 readOnly 옵션이 설정된  경우는 Session의 Flush Mode를 'FlushMode.MANUAL' 모드로 설정한다.

이는 곧, '이 트랜젝션은 커밋 시 flush를 하지 않는다'는 것을 의미한다.

 

결국은 Hibernate는 Entity에 flush를 호출하지 않게 되고, 변경은 자연스럽게 무시되게 된다.

또한, flush가 호출되지 않고, Dirty Checking을 하지 않기 때문에 성능적으로도 이 점을 얻을 수 있다.

(Dirty Checking이 이루어지는 과정을 쉽게 설명한다고 하면, Entity와 Snapshot을 비교하게 되는데 이러한 과정이 생략되게 되면서 성능 상의 이점을 얻게 된다.)

 

댓글1