Transaction Propagation
Transaction Propagation, 트랜잭션 전파는 트랜잭션의 경계에서 이미 진행중인 트랜잭션이 있을 때 또는 없을 때 어떻게 동작할 것인가를 결정하는 방식을 의미한다.
@Transactional
의 propagation
속성을 통해 피호출 트랜잭션 입장에서 호출한 쪽의 트랜잭션을 그대로 사용할 수도 있고, 새롭게 트랜잭션을 생성할 수 도 있다.
Propagation 종류
Transaction Propagation 에는 총 7가지 전파 속성이 존재한다.
REQUIRED
SUPPORTS
MANDATORY
REQUIRES_NEW
NOT_SUPPORTED
NEVER
NESTED
Physical Transaction & Logical Transaction
Physical Transaction
- 물리적 트랜잭션은 실제 트랜잭션이 실행되는 스레드를 의미한다.
Logical Transaction
- 논리적 트랜잭션은 선언적 트랜잭션이 적용된 모든 메서드를 의미한다.
- 즉, 외부 트랜잭션과 내부 트랜잭션의 논리적 범위는 독립적이다.
- 롤백 상태를 개별적으로 결정할 수 있고 이는 물리적 트랜잭션에 매핑된다.
- 따라서, 내부 트랜잭션 설정으로 인해 외부 트랜잭션의 커밋에 영향을 미칠 수 있다.
- 내부 트랜잭션으로 인한 롤백은 외부 트랜잭션 입장에선 예상할 수 없는 롤백이다.
UnexpectedRollbackException
을 발생시킴으로써 외부 트랜잭션이 그 사실을 알 수 있다.
REQUIRED (DEFAULT)
- 부모 트랜잭션이 있다면 부모 트랜잭션에 합류한다.
- 부모 트랜잭션이 없다면 새로운 트랜잭션을 생성한다.
부모 트랜잭션이 있는 경우
ParentService : Parent Transaction Active : 🟢
ChildService : Child Transaction Active : 🟢
PropagationTest : transactions : [ParentService]
부모 트랜잭션이 없는 경우
ParentService : Parent Transaction Active : 🚫
ChildService : Child Transaction Active : 🟢
PropagationTest : transactions : [ChildService]
SUPPORTS
- 트랜잭션이 필요한 처리는 없지만 실패하면 롤백해야 하는 경우 사용하는 전파 속성이다.
- 부모 트랜잭션이 있다면 부모 트랜잭션에 합류한다.
- 부모 트랜잭션이 없다면 트랜잭션을 생성하지 않는다.
부모 트랜잭션이 있는 경우
ParentService : Parent Transaction Active : 🟢
ChildService : Child Transaction Active : 🟢
PropagationTest : transactions : [ParentService]
부모 트랜잭션이 없는 경우
ParentService : Parent Transaction Active : 🚫
ChildService : Child Transaction Active : 🚫
MANDATORY
- 부모 트랜잭션에 의해 항상 피호출되어야 하는 전파 속성이다.
- 부모 트랜잭션이 있다면 부모 트랜잭션에 합류한다.
- 부모 트랜잭션이 없다면 예외를 발생시킨다.
부모 트랜잭션이 있는 경우
ParentService : Parent Transaction Active : 🟢
ChildService : Child Transaction Active : 🟢
PropagationTest : transactions : [ParentService]
부모 트랜잭션이 없는 경우
No existing transaction found for transaction marked with propagation 'mandatory'
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
REQUIRES_NEW
- 무조건 새로운 트랜잭션을 생성한다.
- 항상 물리적 트랜잭션을 새로 생성하기 때문에 내부 트랜잭션이 외부 트랜잭션에 영향을 주지 않는다.
부모 트랜잭션이 있는 경우
ParentService : Parent Transaction Active : 🟢
ChildService : Child Transaction Active : 🟢
PropagationTest : transactions : [ParentService, ChildService]
부모 트랜잭션이 없는 경우
ParentService : Parent Transaction Active : 🚫
ChildService : Child Transaction Active : 🟢
PropagationTest : transactions : [ChildService]
부모 트랜잭션이 예외를 발생시킬 경우
ChildService : Child Transaction Active : 🟢
PropagationTest : transactions : [ChildService]
NOT_SUPPORTED
- 부모 트랜잭션이 있다면 해당 트랜잭션을 보류시키고 트랜잭션이 없는 상태로 처리를 수행한다.
- 부모 트랜잭션이 없다면 트랜잭션을 생성하지 않는다.
부모 트랜잭션이 있는 경우
ParentService : Parent Transaction Active : 🟢
ChildService : Child Transaction Active : 🚫
PropagationTest : transactions : [ParentService]
부모 트랜잭션이 없는 경우
ParentService : Parent Transaction Active : 🚫
ChildService : Child Transaction Active : 🚫
NEVER
- 트랜잭션을 사용하지 않도록 강제하는 속성이다.
- 부모 트랜잭션이 있다면 예외를 발생시킨다.
부모 트랜잭션이 있는 경우
Existing transaction found for transaction marked with propagation 'never'
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
부모 트랜잭션이 없는 경우
ParentService : Parent Transaction Active : 🚫
ChildService : Child Transaction Active : 🚫
NESTED
- 부모 트랜잭션이 있다면 JDBC Savepoint 기능을 이용해 내부에 중첩 트랜잭션을 생성한다.
- 중첩 트랜잭션에서 롤백 발생 시, 해당 중첩 트랜잭션 시작 시점까지만 롤백된다.
- 중첩 트랜잭션은 부모 트랜잭션이 커밋될 때 같이 커밋된다.
- 단일 물리적 트랜잭션에 여러 Savepoint 를 두어 내부 트랜잭션에 롤백이 실행되어도 외부 트랜잭션에서는 물리적 트랜잭션을 계속할 수 있다.
- 부모 트랜잭션이 없다면 새로운 트랜잭션을 생생한다.
부모 트랜잭션이 있는 경우
JpaDialect does not support savepoints - check your JPA provider's capabilities
org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities
- JPA 의 경우 Dirty Checking 을 통한 쓰기 지연 때문에 중첩 트랜잭션을 생성할 수 없어 지원하지 않는다.
부모 트랜잭션이 없는 경우
ParentService : Parent Transaction Active : 🚫
ChildService : Child Transaction Active : 🟢
PropagationTest : transactions : [ChildService]