트랜잭션 옵션
- 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
'Spring' 카테고리의 다른 글
[Spring DB2] 스프링 트랜잭션 전파2 - 활용 (0) | 2024.03.31 |
---|---|
[Spring DB2] 스프링 트랜잭션 전파1 - 기본 (0) | 2024.03.31 |
[Spring DB2] 스프링 트랜잭션 이해 - 1 (2) | 2024.03.27 |
[Spring DB2] 데이터 접근 기술 - 활용 방안 (0) | 2024.03.26 |
[Spring DB2] 데이터 접근 기술 - Querydsl (0) | 2024.03.25 |