요약내용!
@Transactional과 try-catch를 같이 사용하면 예외가 발생해도 트랜잭션이 자동으로 롤백되지 않는 문제가 있습니다.
이는 트랜잭션의 롤백은 @Transactional 메서드가 예외를 던질 때만 발생하고, try-catch로 예외를 잡아버리면 트랜잭션이 정상적으로 커밋되기 때문입니다.
@Transactional와 트랜잭션 처리
트랜잭션 처리는 한 번에 이루어져야 하는 작업의 묶음으로 전체가 완료되거나 실패되는 처리를 의미합니다. (트랜잭션 처리에서 일부 성공은 허용되지 않음)
@Transactional
public void test() {
aService.doA();
bService.doB();
cService.doC();
}
doA(), doB(), doC()를 순차적으로 실행할 때, doC()에서 예외가 발생하면, 앞에서 실행된 작업은 rollback 처리됩니다.
Rollback case
@Transactional
public void execute(Target target) {
updateStatus(target.id());
sendMessages(target); // Exception 발생
}
@Transactional 어노테이션은 데이터베이스의 상태를 변화시키는 작업의 단위를 의미합니다.
updateStatus() 메서드는 데이터베이스에서 특정 데이터의 상태를 변경하고, sendMessages() 메서드로 메시지를 전송합니다.
메시지 전송 시 예외가 발생하면 상태가 변경된 부분이 Rollback 되어 데이터 일관성을 유지합니다.
try-catch & Transactional
@Transactional
public void execute(Target target) {
updateStatus(target.id());
try {
sendMessages(target); // Exception 발생
} catch (RuntimeException e) {
e.printStackTrace();
}
}
Exception을 try-catch 문으로 잡아준 경우에 트랜잭션이 롤백이 작동하는 것을 기대했지만, 롤백이 되지 않고 commit 처리되었습니다.
발생하는 예외를 catch 블록에서 처리되도록 설정했기에 @Transactional에 의한 트랜잭션 처리 루틴이 정상적으로 동작하지 않습니다.
why?
트랜잭션 범위 안에서 발생한 예외를 @Transactional이 아닌 try-catch 문에서 예외를 처리해 주었기 때문에 트랜잭션이 정상적으로 완료가 되어 commit이 됩니다.
If no custom rollback rules are configured in this annotation, the transaction will roll back on RuntimeException and Error but not on checked exceptions.
Transaction Propagation
// Enumeration that represents transaction propagation behaviors for use
public enum Propagation {
...
}
REQUIRED
- default propagation이다.
- 만약 트랜잭션이 활성화돼있거나 존재하지 않으면, 새로운 것을 만들거나 현재 활성화된 트랜잭션에 비즈니스 로직을 추가합니다.
SUPPORTS
- 존재하는 트랜잭션이 있는지 확인하고 있다면 그것을 사용합니다. 만약 없다면 non-transactional 하게 사용합니다.
MANDATORY
- 활성화된 트랜잭션이 있으면 그것을 사용하고, 활성화된 트랜잭션이 없다면 에러를 발생시킵니다.
NEVER
- 만약 활성화된 트랜잭션이 있다면 에러를 발생시킵니다.
NOT_SUPPORTED
- 만약 트랜잭션이 존재한다면 스프링은 그것을 중단시키고 비즈니스 로직을 트랜잭션 없이 동작시킵니다.
REQUIRES_NEW
- 만약 트랜잭션이 현재 존재하면 중단시키고 새로운 것을 생성합니다.
참고자료
Transaction Propagation and Isolation in Spring @Transactional | Baeldung