알면 알수록 어려운 트랜잭션의 세계
Spring 트랜잭션을 처리하다 보면 Transaction marked as rollbackOnly 예외를 종종 접하게 됩니다. 트랜잭션 안에서 RuntimeException이 발생해서 트랜잭션이 rollbackOnly로 마킹이 되어 무조건 Rollback이 되는 것입니다.
어라 try-catch 해서 처리했는데?
비록 try-catch 에서 예외를 처리했다 하더라도 RuntimeException 이 트랜잭션 안에서 발생하면 무조건 Rollback으로 표시(mark)가 됩니다. 그럼 위 상황을 우회하려면 어떻게 해야 할까요?
아래 소스의 historyRepository.save() 에서 DataIntegrityViolationException 이 발생한다고 가정하면 main() 은 sub1()과 sub2()가 모두 실행되지만 commit 시에 예외가 발생하고 트랜잭션이 rollback됩니다. 즉 TransactionSystemException(message="Transaction marked as rollbackOnly") 을 throw 받게 되는 것입니다.
@Transactional
public void main() {
sub1();
sub2();
}
@Transactional
public void sub1() {
try {
logRepository.save();
historyRepository.save(); // 예외가 발생한다고 가정
} catch (DataIntegrityViolationException ignore) {
}
}
@Transactional
public void sub2(){
eventRepository.save();
}
sub1() 에서 try-catch를 했다는 것은 안에서 DataIntegrityViolationException 이 발생해도 무시하고 트랜잭션을 처리하겠다는 의도입니다. 의도에 맞게 수정해 보면
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sub1() {
try {
// ...
} catch (DataIntegrityViolationException ignore) {
}
}
@Transactional
public void main() {
try{
sub1();
} catch (TransactionSystemException ignore) {
}
sub2();
}
@Transactional
public void sub1() {
try {
// ...
} catch (DataIntegrityViolationException ignore) {
}
}
@Transactional의 속성 중에 하나인 noRollbackFor는 명시를 하면 해당 Exception이 발생해도 Rollback이 되지 않습니다.
단, 주의할 점은 callee 에게 Exception은 그대로 throws 되므로, callee에서 예외처리를 반드시 해줘야 한다는 것입니다.