스프링/스프링 기초DB
체크예외와 언체크(런타임) 예외에서의 트랜잭션
chanhee01
2023. 4. 1. 15:48
스프링에서 체크 예외와 언체크 예외에서의 트랜잭션이 다르게 진행된다.
체크 예외 : 비즈니스 의미가 있을 때 사용
언체크 예외 : 복구 불가능한 예외
체크 예외일 때는 트랜잭션이 커밋되고 언체크 예외에서는 트랜잭션이 롤백된다.
@Transactional(rollbackFor = MyException.class)
물론 체크 예외에서도 롤백을 하고 싶다면 위와 같이 rollbackFor을 넣어주면 롤백을 하게된다.
OrderService.class
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
// JPA는 트랜잭션 커밋 시점에 Order 데이터를 DB에 반영한다.
@Transactional
public void order(Order order) throws NotEnoughMoneyException {
log.info("order 호출");
orderRepository.save(order);
log.info("결제 프로세스 진입");
if (order.getUsername().equals("예외")) {
log.info("시스템 예외 발생");
throw new RuntimeException("시스템 예외");
} else if (order.getUsername().equals("잔고부족")) {
log.info("잔고 부족 비즈니스 예외 발생");
order.setPayStatus("대기");
throw new NotEnoughMoneyException("잔고가 부족합니다");
} else {
// 정상 승인
log.info("정상 승인");
order.setPayStatus("완료");
}
log.info("결제 프로세스 완료");
}
}
NotEnoughMondyException는 별도로 만든 체크 예외이다.
런타임 예외가 발생했을 때에 롤백이 무조건 이루어진다. 체크 예외가 발생했을 때에는 커밋이 이루어지며 별도의 설정이 있지 않다면 롤백되지 않는다.
@Test
void runtimeException() {
// given
Order order = new Order();
order.setUsername("예외");
// when
Assertions.assertThatThrownBy(() -> orderService.order(order))
.isInstanceOf(RuntimeException.class);
// then
Optional<Order> orderOptional = orderRepository.findById(order.getId());
assertThat(orderOptional.isEmpty()).isTrue();
// 롤백되어서 데이터가 없어짐
}
테스트코드에서 런타임 예외가 발생했을 때를 실행시켜보면 롤백되어서 아예 데이터가 존재하지 않는다.
@Test
void bizException() {
// given
Order order = new Order();
order.setUsername("잔고부족");
// when
try {
orderService.order(order);
} catch (NotEnoughMoneyException e) {
log.info("고객에게 잔고 부족을 알리고 별도의 계좌로 입금하도록 안내");
}
// then
Order findOrder = orderRepository.findById(order.getId()).get();
assertThat(findOrder.getPayStatus()).isEqualTo("대기");
}
반면에 잔고부족인 경우의 체크 예외가 발생했을 때에는 롤백되지 않고 데이터가 남아져있으며 고객에서 별도의 안내를 하도록 try-catch가 된 것을 확인할 수 있다.
런타임 예외는 복구 불가능한 예외로 인지되기때문에 무조건 롤백이 되는 반면에 체크 예외는 롤백이 안되고 커밋된다는 것을 알 수 있다.