트랜잭션 전파 전략 7개
트랜잭션 전파 전략은 총 7가지가 있습니다. REQUIRED, REQUIRES_NEW, NESTED, MANDATORY, NEVER, SUPPORTS, NOT SUPPORTED를 하나씩 알아보겠습니다.
REQUIRED
종속 된 트랜잭션은 현재의 트랜잭션을 따라갑니다. 만약에 현재 트랜잭션이 없다면 새로운 트랜잭션을 만듭니다. 트랜잭션 어노테이션의 기본 설정이기도 합니다.
트랜잭션 기본 설정입니다.
기존 트랜잭션 있음 : 기존 트랜잭션에 참여합니다
기존 트랜잭션 없음 : 트랜잭션 없이 진행합니다
REQUIRES_NEW
종속된 트랜잭션은 언제나 새로운 트랜잭션을 만들고 만약에 트랜잭션이 존재한다면 현재 트랜잭션을 잠시 지연시킵니다. 실제 트랜잭션 지연이 모든 트랜잭션 매니저에서 발생하는 것은 아닙니다. javax.transaction.TransactionManger를 지원하는 JtaTransactionManager에만 적용됩니다.
항상 새로운 트랜잭션을 생성합니다.
기존 트랜잭션 있음 : 새로운 트랜잭션을 생성합니다
기존 트랜잭션 없음 : 새로운 트랜잭션을 생성합니다
NESTED
만약, 현재 트랜잭션이 존재한다면 해당 트랜잭션을 이어서 실행합니다.(REQUIRED와 동일) NESTED 트랜잭션의 실제 생성은 JDBC DataSourceTransactionManager라는 특별한 트랜잭션 매니저만 가능합니다. (JPA에서는 사용이 불가능)
중첩된 트랜잭션을 사용하겠다는 의미입니다.
기존 트랜잭션 있음 : 중첩 트랜잭션을 만듭니다
- 중첩 트랜잭션은 외부 영향을 받지만 중첩 트랜잭션이 외부에 영향을 주지는 않습니다
- 중첩 트랜잭션이 롤백되어도 외부 트랜잭션은 독립적으로 커밋할 수 있습니다
- 외부 트랜잭션이 롤백되면 중첩 트랜잭션은 롤백됩니다
기존 트랜잭션 없음 : 새로운 트랜잭션을 생성합니다
중첩 트랜잭션은 JPA에서는 사용 할 수 없습니다.
MANDATORY
현재 트랜잭션을 지원하는데 아무런 트랜잭션이 없으면 예외를 발생합니다.
트랜잭션이 의무(MANDATORY)입니다.
기존 트랜잭션 있음 : 기존 트랜잭션에 참여합니다
기존 트랜잭션 없음 : IllegalTransactionStateException 예외를 발생합니다.
NEVER
어떠한 트랜잭션 관련 설정도 실행하지 않으며, 트랜잭션이 존재하면 예외를 발생시킵니다.
트랜잭션을 사용하지 않겠다는 의미입니다. 기존 트랜잭션도 예외를 발생시킵니다
기존 트랜잭션 있음 : IllegalTransactionStateException 예외를 발생합니다.
기존 트랜잭션 없음 : 트랜잭션 없이 진행합니다
SUPPORTS
현재의 트랜잭션을 따라갑니다. 만약에 아무런 트랜잭션이 없으면 트랜잭션 없이 실행합니다.
실제 트랜잭션 지연이 모든 트랜잭션 매니저에서 가능한 것은 아닙니다. javax.transaction.TransactionManger를 지원하는 JtaTransactionManager에서만 가능합니다.
현재 트랜잭션을 실행하는데, 만약에 아무런 트랜잭션이 없다면 트랜잭션을 실행하지 않습니다.
기존 트랜잭션 있음 : 기존 트랜잭션에 참여합니다
기존 트랜잭션 없음 : 트랜잭션 없이 진행합니다
NOT SUPPORTED
트랜잭션을 사용하지 않고 실행합니다, 만약에 현재 트랜잭션이 존재하면 지연시킵니다.
실제 트랜잭션 지연이 모든 트랜잭션 매니저에서 가능한 것은 아닙니다. javax.transaction.TransactionManger를 지원하는 JtaTransactionManager에서만 가능합니다.
트랜잭션을 지원하지 않습니다.
기존 트랜잭션 있음 : 트랜잭션 없이 진행합니다
기존 트랜잭션 없음 : 트랜잭션 없이 진행합니다(기존 트랜잭션 보류)
내부 롤백이 왜 전체 롤백이 될까?
내부에서 롤백이 발생하면, 트랜잭션 동기화 매니저에 rollbackOnly=true를 표시합니다. 외부 트랜잭션은 실제 커밋을 호출하기 위해 트랜잭션 동기화 매니저에서 커넥션을 획득해야 하는데 rollbackOnly=true로 표시되어 있기 때문에 커밋을 하지 못하고 롤백을 합니다. 또한 단순히 롤백 할 뿐만 아니라 UnexpectedRollbackException을 던집니다. 시스템 입장에서는 커밋을 호출했지만, 롤백이 되었다는 것을 명확히 알려주기 위함입니다.
내부 롤백이 되더라도 외부에 영향을 주지 않기 위해서는 REQUIRES_NEW 정책을 사용해야 합니다. 외부와 내부 각각 별도의 트랜잭션이 생성되기 때문에 서로 독립적이므로 롤백이 영향을 주지 않습니다.(Suspending current transaction)
다른 정책의 트랜잭션 중첩은 트랜잭션 동기화 매니저에서 1개의 커넥션을 사용하지만, REQUIRES_NEW 정책의 경우 각각 커넥션을 생성하여 총 2개의 커넥션을 사용합니다. 커넥션을 2개나 사용한다는 점을 주의해야 합니다.
핵심은 전파 속성에 따른 "롤백 유무"이다
스프링 트랜잭션 전파의 핵심은 롤백이 아닐까 생각합니다. 중첩된 여러개의 트랜잭션이 롤백될 때 어떻게 동작하는지 이해하고 설계하는 것이 핵심입니다. NESTED는 내부의 예외가 외부까지 전파가 되지 않는데, 해당 기능은 JPA에는 적용이 되지 않는 단점이 있습니다.
이와 관련해서 이미 우아한 형제에서는 관련 포스팅이 있습니다.
응? 이게 왜 롤백되는 거지? (https://techblog.woowahan.com/2606/)
예외 발생 시, 예외를 처리하는 @ControllerAdvice를 선택할 것인지, 나를 호출한 곳으로 다시 예외를 던질지, 아니면 애초에 서비스 로직을 분리해서 서로가 트랜잭션 전파에 영향이 없도록 할 것인지 전략을 세우고 그에 따라 전파 수준을 결정하면 됩니다.
'Spring > Spring JPA' 카테고리의 다른 글
스프링 트랜잭션 추상화, 동기화 (0) | 2022.11.30 |
---|---|
스프링 트랜잭션 전파 기본개념 (0) | 2022.11.17 |
OSIV (0) | 2022.10.01 |
일반 Join vs Fetch Join (0) | 2022.06.29 |
JPA를 이용해 History 테이블 자동 생성하기 (1) | 2021.12.14 |