Spring Boot @Transactional 어노테이션을 이해하기 위해 @Transactional 어노테이션이 무엇이며 제공하는 설정은 무엇인지 알아보겠습니다.
1편에 이어서 2편에서는 @Transactional 어노테이션이 제공해 주는 트랜잭션 롤백(Transaction Rollback) 규칙과 , 시간제한(Transaction Timeout), readOnly Flag와 JPA에서의 ReadOnly Flag에 특징들에 대해서 이어서 살펴보도록 하겠습니다.
1편을 아직 못 보신 분들은 아래 링크를 참조해주시면 감사합니다.
https://colevelup.tistory.com/34
아래 예제들은 user entity 와 user repository가 간단하게 있다고 가정하고 진행합니다.
Timeout
모든 트랜잭션에 대해 @Transactinal 어노테이션을 사용하여 트랜잭션 timeout 을 제공할 수 있습니다.
트랜잭션 시간 제한(timeout = 10)을 정의하면 트랜잭션이 주어진 시간 내에 완료되어야 하며 그렇지 않으면 트랜잭션을 롤백시키며 트랜잭션 예외(트랜잭션 시간 만료 오류)가 발생한다고 말합니다.
시간 값 유형은 정수여야 하며 밀리초 단위로 간주됩니다.
timeout 설정값의 default는 -1입니다
즉 default로 timeout이 지원되지 않습니다.
@Service
public class UserServiceTimeOut {
@Autowired
private UserRepository userRepository;
@Transactional(timeout = 10)
public void myTransactionalMethod() {
// Do some database operations that might take some time
userRepository.findAll();
userRepository.save(new User());
// ...
}
}
readOnly Flag
readOnlyFlag를 활용하면 트랜젝션을 읽기 전용으로 설정 가능합니다.
baldung에서는 사실 읽기 전용 플래그가 설정된 경우 Insert 또는 Update가 발생하지 않는다고 확신할 수 없으며 이 동작은 database vendeor에 따라서 달라진다고 말합니다.
또한 트랜잭션 컨텍스트 외부에서 작업이 발생하면 플래그가 무시된다고 합니다.
@Service
public class UserServiceReadOnly {
@Autowired
private UserRepository userRepository;
@Transactional(readOnly = true)
public List<User> getUsers() {
return StreamSupport.stream(userRepository.findAll().spliterator(), false)
.collect(Collectors.toList());
}
}
JPA에서의 readOnly = true
@Transactional에 readOnly = true 옵션을 주면 스프링 프레임워크가 세션 플러시 모드를 MANUAL로 설정하며 강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않게 되어서, 트랜잭션이 커밋되면서 실수로 엔티티가 등록, 수정, 삭제되는 일을 방지할 수 있습니다.
readOnly = true 설정의 장점
실수로 인한 수정 방지
트랜잭션이 읽기 전용으로 표시되면 해당 트랜잭션 내에서 엔티티를 수정하려고 시도하면 예외가 발생합니다.
이렇게 하면 실수로 엔티티를 수정하는 것을 방지하고 데이터의 일관성을 유지할 수 있습니다.
성능 향상
트랜잭션이 읽기 전용으로 표시되면 JPA는 엔티티의 스냅샷을 저장하거나 변경 감지를 수행할 필요가 없습니다.
따라서 특히 많은 엔티티에 액세스하지만 수정되는 엔티티는 거의 없는 상황에서 상당한 성능 향상을 가져올 수 있습니다.
readOnly = true 설정의 한계
읽기 전용 트랜잭션의 한계
트랜잭션이 읽기 전용인 경우, 영속성 컨텍스트 내에서 엔티티를 관리할 수 없습니다.
즉, 기본 캐시 또는 지연 로딩과 같은 기능을 사용할 수 없습니다.
엔티티에서 지연 로드된 프로퍼티에 액세스 하려고 하면 예외가 발생합니다.
읽기 전용 트랜잭션이 모든 상황에 적합한 것은 아니라는 점에 유의해야 합니다.
트랜잭션 내에서 데이터를 수정해야 하는 경우 읽기 전용 트랜잭션을 사용해서는 안 됩니다.
또한 특정 상황에서 읽기 전용 트랜잭션이 상당한 이점을 제공한다는 것을 측정하고 확인하지 않는 한 성능 최적화를 위해 읽기 전용 트랜잭션을 사용해서는 안 됩니다.
영속성 컨텍스트와 캐싱에 대해서 헷갈리신다면 이전 포스팅을 참조해 주시면 감사합니다.
https://colevelup.tistory.com/21
https://colevelup.tistory.com/22
트랜잭션 롤백 (Rollback Rules)
roolbackFor 및 noRollbackFor annotation Parameter를 사용하여 트랜잭션 rollback을 할지 안 할지에 대한 설정값을 줄 수 있습니다.
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional(rollbackFor = { SQLException.class })
public void updateUser(User user) throws SQLException {
userRepository.save(user);
}
@Transactional(noRollbackFor = { NullPointerException.class })
public User getUserById(Long id) throws NullPointerException {
return userRepository.findById(id)
.orElseThrow(() -> new NullPointerException("User not found"));
}
}
class 수준에서 @Transactional을 사용하여 UserService 클래스의 모든 메서드가 트랜잭션 내에서 실행되도록 지정
updateUser() 메서드에는 메서드 실행 중에 SQLException이 발생하는 경우 트랜잭션을 롤백하도록 지정
getUserById() 메서드에도 @Transactional 어노테이션이 지정되어 있으며,
NullPointerException으로 인해 트랜잭션이 롤백되지 않도록 지정
Try Catch로 exception 을 Catch 한다면?
트랜잭션 메서드가 실행되면 Spring Boot는 메서드를 트랜잭션으로 래핑 합니다.
메서드 실행 중에 예외가 발생하면 Spring Boot는 기본적으로 트랜잭션을 롤백합니다.
예외가 try-catch 블록 내에서 포착되어 처리되는 경우에도 트랜잭션 관리자는 트랜잭션을 롤백합니다.
트랜잭션 메서드 실행 중에 발생하는 uncheckedException(즉, RuntimeException과 그 서브클래스) 또는 error는 default로 롤백 대상으로 간주됩니다.
트랜잭션 관리자가 트랜잭션을 롤백하지 못하도록 하려면 위에서 사용한 noRollbackFor 속성을 사용하여 롤백을 트리거해서는 안 되는 예외를 지정하여 롤백을 하지 않을 수 있습니다.
예시
@Transactional(noRollbackFor = { CustomException.class })
public void myTransactionalMethod() throws CustomException {
try {
// code that may throw CustomException
} catch (CustomException e) {
// exception handling code
}
}
참조
https://www.baeldung.com/transaction-configuration-with-jpa-and-spring
https://dzone.com/articles/how-does-spring-transactional
'Framework > Spring' 카테고리의 다른 글
[Spring] SpringWebFlux에서의 효과적 Error처리와 백프레셔(BackPressure) (0) | 2023.04.01 |
---|---|
[Spring] Tomcat VS Netty Connector - NIO(non-blocking I/O) or BIO(Blocking I/O) (0) | 2023.03.26 |
[Spring] Project Reactor EventLoop와 Flux와 Mono. (2) | 2023.03.21 |
[Spring] Reactive Programming 개요 (1) | 2023.03.19 |
[Spring] @Transactional 어노테이션 이해하기(1) 전파유형(Propagation) 과 격리수준(Isolation) (0) | 2023.03.11 |
댓글