GitHub

https://github.com/Choidongjun0830

Spring

[Spring DB2] 스프링 트랜잭션 이해 - 2

gogi masidda 2024. 3. 28. 21:41

트랜잭션 옵션

  • value, transactionManager
    • 트랜잭션을 사용하려면 먼저 스프링 빈에 등록된 어떤 트랜잭션 매니저를 사용할 지 알아야 한다. 
    • 코드로 직접 트랜잭션을 사용할 때 트랜잭션 매니저를 주입 받아서 사용했다.
    • @Transactional에서도 트랜잭션 프록시가 사용할 트랜잭션 매니저를 지정해주어야 한다.
    • value와 transactionManager 둘 중 하나에 스프링 빈 이름을 적어주면 트랜잭션 매니저가 지정되는데, 이 값을 생략하면 기본으로 등록된 트랜잭션 매니저를 사용한다. 그래서 대부분 생략한다.
    • 트랜잭션 매니저가 둘 이상이면 적어준다.
  • rollbackFor
    • 예외 발생 시 스프링 트랜잭션의 기본 정책은 언체크 예외인 'RuntimeException', 'Error'와 이 하위 예외가 발생하면 롤백한다. 체크 예외인 'Exception'과 그 하위 예외들은 커밋하는 것이다.
    • 이 옵션을 사용하면 기본 정책에 추가로 어떤 예외가 발생했을 때 롤백할지 지정할 수 있다.
  • noRollbackFor
    • 기본 정책에 추가로 어떤 예외가 발생했을 때 롤백하면 안되는지 지정할 수 있다. 
  • propagation
    • 트랜잭션 전파에 대한 옵션. 다음 강의에서 배운다네요.
  • isolation
    • 트랜잭션 격리 수준을 정할 수 있다.
    • 기본 값은 데이터베이스에서 설정한 트랜잭션 격리 수준을 사용하는 'DEFAULT'이다.
    • 개발자가 직접 트랜잭션 격리 수준을 지정하는 것은 드물다. 
  • timeout
    • 트랜잭션 수행 시간에 대한 타임아웃을 초 단위로 지정한다.
  • label
    • 트랜잭션 애노테이션에 있는 값을 직접 읽어서 어떤 동작을 하고 싶을 때 사용할  수 있지만, 일반적으로 사용하지는 않는다. 
  • readOnly
    • 트랜잭션은 기본적으로 읽기와 쓰기가 모두 가능한 트랜잭션이 생성된다.
    • readOnly = True 옵션을 사용하면 읽기 전용 트랜잭션이 생성되고, 등록, 수정, 삭제가 안되고 읽기 기능만 작동한다. 그리고 readOnly 옵션을 사용하면 읽기에서 다양한 성능 최적화가 발생할 수 있다.
    • readOnly 옵션은 크게 3곳에서 적용
      • 프레임워크
        • JdbcTemplate는 읽기 전용 트랜잭션 안에서 변경 기능을 실행하면 예외를 던진다.
        • JPA는 읽기 전용 트랜잭션의 경우 커밋 시점에 플러시를 호출하지 않는다. 읽기 전용이니까 변경에 사용되는 플러시를 호출할 필요가 없다. 이런 동작들을 하지 않아서 최적화가 발생한다.
      • JDBC 드라이버
        • DB와 드라이버 버전에 따라서 다르게 동작해서 사전에 확인이 필요하다.
        • 읽기 전용 트랜잭션에서 변경이 발생하면 예외를 던진다.
        • 읽기, 쓰기 데이터베이스를 구분해서 요청한다. 읽기 전용 트랜잭션의 경우 읽기 데이터베이스의 커넥션을 획득해서 사용한다. 
      • 데이터베이스
        • 내부에서 성능 최적화가 발생한다. 

예외와 트랜잭션 커밋, 롤백

application.properties에 아래와 같이 적어줌으로써 로그에서 커밋되는지 롤백되는지 알 수 있다.

logging.level.org.springframework.transaction.interceptor=TRACE
logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG
#JPA loglogging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG
logging.level.org.hibernate.resource.transaction=DEBUG ```
참고로 지금은 JPA를 사용하므로 트랜잭션 매니저로 JpaTransactionManager 가 실행
@SpringBootTest
public class RollbackTest {
    @Autowired
    RollbackService rollbackService;

    @Test
    void runtimeException() {
        Assertions.assertThatThrownBy(() -> rollbackService.runtimeException())
                .isInstanceOf(RuntimeException.class);
    }

    @Test
    void checkedException() throws MyException {
        Assertions.assertThatThrownBy(() -> rollbackService.checkedException())
                .isInstanceOf(MyException.class);
    }

    @Test
    void rollbackFor() throws MyException {
        Assertions.assertThatThrownBy(() -> rollbackService.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 checkedException");
            throw new MyException();
        }
    }

    static class MyException extends Exception {

    }
}
  • 런타임 예외는 롤백되고, 체크 예외는 커밋, rollbackFor로 지정하면 롤백되는 것을 알 수 있었다.

 

왜 체크 예외는 커밋하고, 런타임 예외는 롤백할까?

  • 체크 예외 : 비즈니스 의미가 있을 때 사용
    • 주문시 결제 잔고 부족하면 결제 상태를 대기로 처리하고, 고객에게 잔고 부족을 알리고 별도의 계좌로 입금하도록 안내 -> 롤백하면 고객의 주문정보가 사라지기 때문에 롤백하면 안됨.
    • 시스템에 문제가 있는게 아니라, 비즈니스 상황에서 문제가 되기 때문에 발생한 예외. 
  • 런타임 예외 : 복구 불가능한 예외 
    • 네트워크 통신 X, SQL 문법 오류 ...
728x90