예외가 발생했는데, 내부에서 예외를 처리하지 못하고, 트랜잭션 범위( @Transactional가 적용된 AOP ) 밖으로 예외를 던지면 어떻게 될까?

예외 발생시 스프링 트랜잭션 AOP는 예외의 종류에 따라 트랜잭션을 커밋하거나 롤백한다.
RollbackTest
package hello.springtx.exception;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest
public class RollbackTest {
@Autowired
RollbackService service;
@Test
void runtimeException() {
Assertions.assertThatThrownBy(() -> service.runtimeException())
.isInstanceOf(RuntimeException.class);
}
@Test
void checkedException() {
Assertions.assertThatThrownBy(() -> service.checkedException())
.isInstanceOf(MyException.class);
}
@Test
void rollbackFor() {
Assertions.assertThatThrownBy(() -> service.rollbackFor())
.isInstanceOf(MyException.class);
}
@TestConfiguration
static class RollbackTestConfig {
@Bean
RollbackService rollbackService() {
return new RollbackService();
}
}
@Slf4j
static class RollbackService {
//런타임 예외 발생:롤백
@Transactional
public void runtimeException() {
log.info("call runtimeException");
throw new RuntimeException();
}
//체크 예외 발생:커밋
@Transactional
public void checkedException() throws MyException {
log.info("call checkedException");
throw new MyException();
}
//체크 예외 rollbackFor 지정:롤백
@Transactional(rollbackFor = MyException.class)
public void rollbackFor() throws MyException {
log.info("call rollbackFor");
throw new MyException();
}
}
static class MyException extends Exception {
}
}
실행하기 전에 다음을 추가하자. 이렇게 하면 트랜잭션이 커밋되었는지 롤백 되었는지 로그로 확인할 수 있다.
application.properties
logging.level.org.springframework.transaction.interceptor=TRACE
logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=
DEBUG
#JPA log
logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG
logging.level.org.hibernate.resource.transaction=DEBUG
참고로 지금은 JPA를 사용하므로 트랜잭션 매니저로 JpaTransactionManager 가 실행되고, 여기의 로그를 출력하게 된다.
runtimeException() 실행 - 런타임 예외
//런타임 예외 발생: 롤백
@Transactional
public void runtimeException() {
log.info("call runtimeException");
throw new RuntimeException();
}
실행 결과
Getting transaction for [...RollbackService.runtimeException]
call runtimeException
Completing transaction for [...RollbackService.runtimeException] after
exception: RuntimeException
Initiating transaction rollback
Rolling back JPA transaction on EntityManager
checkedException() 실행 - 체크 예외
//체크 예외 발생: 커밋
@Transactional
public void checkedException() throws MyException {
log.info("call checkedException");
throw new MyException();
}
실행 결과
Getting transaction for [...RollbackService.checkedException]
call checkedException
Completing transaction for [...RollbackService.checkedException] after
exception: MyException
Initiating transaction commit
Committing JPA transaction on EntityManager
rollbackFor
이 옵션을 사용하면 기본 정책에 추가로 어떤 예외가 발생할 때 롤백할 지 지정할 수 있다.
@Transactional(rollbackFor = Exception.class)
예를 들어서 이렇게 지정하면 체크 예외인 Exception 이 발생해도 커밋 대신 롤백된다. (자식 타입도 롤백된다.)
rollbackFor() 실행 - 체크 예외를 강제로 롤백
//체크 예외 rollbackFor 지정: 롤백
@Transactional(rollbackFor = MyException.class)
public void rollbackFor() throws MyException {
log.info("call rollbackFor");
throw new MyException();
}
실행 결과
Getting transaction for [...RollbackService.rollbackFor]
call rollbackFor
Completing transaction for [...RollbackService.rollbackFor] after exception:
MyException
Initiating transaction rollback
Rolling back JPA transaction on EntityManager