트랜잭션은 Spring AOP를 통해 구현되어있다.
따라서 클래스나 메소드에 @Transactional이 선언되면 해당 클래스에 트랜잭션이 적용된 프록시 객체가 생성 된다.
프록시객체는 @Transactional 이 포함된 메서드가 호출될 경우, 트랜잭션을 시작하고 Commit 혹은 Rollback을 수행하게 된다.
🚩 CheckedException or 예외가 없을 때는 Commit
🚩 UncheckedException이 발생하면 Rollback
@Transactional 의 propagation을 통해 전파를 설정할 수 있다.
Spring이 제공하는 트랜잭션 어노테이션 기능의 장점 중 하나는 여러 트랜잭션 적용 범위를 묶어 커다란 하나의 트랜잭션 경계를 만들 수 있다는 점이다. 우리는 Spring이 트랜잭션을 어떻게 진행시킬지 결정하도록 전파 속성을 전달해야 하는데 이를 통해 새로운 트랜잭션을 시작할지 또는 기존의 트랜잭션에 참여할지 등을 결정하게 된다.
기본 설정은 REQUIRED 이다. 트랜잭션A(부모) 내부에 트랜잭션B(자식)가 실행되는 경우, 트랜잭션A에 트랜잭션 B가 합류한다. 다시 말해 커밋이나 롤백의 기준이 트랜잭션 A만 존재하는 경우와 같다
A,B 트랜잭션을 서로 부모 자식간의 관계 상관없이 개별 트랜잭션으로 취급한다. 각각 커밋과 롤백이 이루어진다.
부모에 자식이 합류한다. 다만, 부모 트랜잭션이 없으면 에러가 발생한다.
부모에 자식이 nested 된다. 부모 내부에서 롤백 시, 자식의 시작지점까지만 롤백되는 방식이다. 커밋은 부모 트랜잭션이 커밋될 때 함께 커밋된다. 부모 트랜잭션이 없다면 새로운 트랜잭션을 생성한다.
트랜잭션을 생성하지 않는다. 만약 부모 트랜잭션이 존재하면 예외를 발생시킨다.
부모가 있다면 합류하고 부모가 없다면 트랜잭션을 생성하지 않는다.
부모 트랜잭션이 있다면 보류시킨다. 부모 트랜잭션이 없으면 트랜잭션을 생성하지 않는다.
스프링 프레임워크에서 어노테이션으로 트랜잭션을 읽기 전용 모드로 설정할 수 있다.
@Transactional(readOnly = true)
읽기 전용 쿼리의 성능 최적화
엔티티가 영속성 컨텍스트에 관리되면 1차 캐시부터 변경감지까지 얻을 수 있는 혜택이 많다. 하지만 영속성 컨텍스트는 변경 감지를 위해서 스냅샷 인스턴스를 보관하므로 더 많은 메모리를 사용하는 단점이 있다.
따라서 조회만 하는 경우에 읽기 전용으로 엔티티를 조회하면 메모리 사용량을 최적화할 수 있다.
트랜잭션에 readOnly = true 옵션을 주면 스프링 프레임워크가 하이버네이트 세션 플러시 모드를 MANUAL로 설정한다.
이렇게 하면 강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않는다.
따라서 트랜잭션을 커밋하더라도 영속성 컨텍스트가 플러시 되지 않아서 엔티티의 등록, 수정, 삭제가 동작하지 않고 읽기 전용으로 영속성 컨텍스트는 변경 감지를 위한 스냅샷을 보관하지 않으므로 성능이 향상된다.