스프링/스프링 기초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가 된 것을 확인할 수 있다.

 

 

런타임 예외는 복구 불가능한 예외로 인지되기때문에 무조건 롤백이 되는 반면에 체크 예외는 롤백이 안되고 커밋된다는 것을 알 수 있다.