본문 바로가기
Framework/Spring

[Spring] @Transactional 어노테이션 이해하기(2) 롤백(rollback) 규칙, 시간 제한(Timeout), readOnly Flag

by 곰민 2023. 3. 12.

Spring Boot @Transactional 어노테이션을 이해하기 위해 @Transactional 어노테이션이 무엇이며 제공하는 설정은 무엇인지 알아보겠습니다.

1편에 이어서 2편에서는 @Transactional 어노테이션이 제공해 주는 트랜잭션 롤백(Transaction Rollback) 규칙과 , 시간제한(Transaction Timeout), readOnly Flag와 JPA에서의 ReadOnly Flag에 특징들에 대해서 이어서 살펴보도록 하겠습니다.


1편을 아직 못 보신 분들은 아래 링크를 참조해주시면 감사합니다.
https://colevelup.tistory.com/34

 

[Spring boot] @Transactional 어노테이션 이해하기(1) 전파유형(Propagation) 과 격리수준(Isolation)

Spring Boot @Transactional 어노테이션을 이해하기 위해 @Transactional 어노테이션이 무엇이며 제공하는 설정은 무엇인지. 1편에서는 @Transactional 어노테이션이 제공하는 트랜잭션 전파(Transaction Propagation)

colevelup.tistory.com

 

2편에서도 어김없이 등장한 스파이더맨 ??????

 

아래 예제들은 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());
        // ...
    }

}

 

Transaction Timeout

 

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

 

[JPA] Persistence context(영속성 컨텍스트)와 EntityManager

JPA와 ORM에 대한 간략한 정의와 JPA에서 중요한 영속성 컨텍스트(Persistence Context)와 엔티티 매니저(Entity Manager), 영속성 컨텍스트 타입(Persistence Context type), 영속성 컨텍스트(Persistence Context)의 장점

colevelup.tistory.com

https://colevelup.tistory.com/22

 

[JPA] 엔티티(Entity) 생명주기, 1차캐시, 변경 감지(Dirty-Checking)

JPA에서 엔티티(Entity)의 생명주기와 , 1차 캐시(First-Level-Cache)와 1차 캐시가 갖는 장점들, 변경감지(Dirty-Checking)와 플러시(Flush())가 내부적으로 어떻게 동작하는지에 대해서 알아보도록 하겠습니다

colevelup.tistory.com

 

트랜잭션 롤백 (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://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html

https://www.baeldung.com/transaction-configuration-with-jpa-and-spring

https://dzone.com/articles/how-does-spring-transactional

https://vladmihalcea.com/spring-transactional-annotation/

https://techblog.woowahan.com/2606/

반응형

댓글