Spring Boot

@Transactional

iksadnorth 2023. 7. 30. 18:55

👣 개요

Spring AOP를 기반으로 작동되는 Spring의 트랜잭션 처리 Annotation이다.

// 트랜잭션 시작
EntityTransaction transaction = entityManager.getTransaction();

transaction.begin();
try {
    // 트랜잭션 범위 내에서 실행할 코드 작성
    
    // 성공적으로 실행되면 커밋
    transaction.commit();
    
} catch (Throwable e) {
    // 예외가 발생하면 롤백
    transaction.rollback();
    throw e;
    
}

위 과정을 @Transactional 어노테이션으로 대체할 수 있다.

 

👣 트랜잭션 전파

'@Transactional을 적용한 메서드 A' 내부에 또다른 '@Transactional을 적용한 메서드 B'를 실행할 때,
해당 메서드가 어떻게 동작할지에 대한 규칙에 대한 내용이 '트랜잭션 전파'다.

  1. REQUIRED (기본값)
    메서드가 실행될 때 이미 존재하는 트랜잭션[메서드 A]이 있으면 해당 트랜잭션에 참여하며,
    없을 경우 새로운 트랜잭션을 시작한다. 내부 트랜잭션[메서드 B]은 어쨋든 트랜잭션을 시작한다.
    내부 트랜잭션[메서드 B]이 정상적으로 처리가 되었어도 외부 트랜잭션[메서드 A]에서 장애가 발생하면
    정상 처리된 내부 트랜잭션[메서드 B]도 취소된다. [Atomic 특징 반영]
  2. REQUIRES_NEW
    항상 새로운 트랜잭션을 시작하며, 이미 존재하는 트랜잭션과는 독립적으로 실행된다.
    따라서 이 옵션을 사용하면 메서드 실행 중 다른 트랜잭션과의 연관이 끊어지게 된다.
    내부 트랜잭션[메서드 B]에서 장애가 발생해도 외부 트랜잭션[메서드 A]은 자기 할일을 한다.
    내부 트랜잭션[메서드 B]에 의해 롤백되지 않는다는 것이다. 그리고 그 반대의 경우에도 해당된다.
  3. SUPPORTS
    이미 존재하는 트랜잭션이 있으면 해당 트랜잭션에 참여하며,
    없을 경우 트랜잭션 없이 실행된다. 즉, 트랜잭션 없이도 실행 가능한 옵션이다.
  4. MANDATORY
    이미 존재하는 트랜잭션이 있을 경우 해당 트랜잭션에 참여하며, 없을 경우 예외를 발생시킨다.
    즉, 메서드 실행 시 반드시 이미 트랜잭션이 존재해야 한다.
  5. NOT_SUPPORTED
    트랜잭션 없이 실행되며, 이미 존재하는 트랜잭션이 있을 경우 해당 트랜잭션을 일시 중지시킨다.
  6. NEVER
    트랜잭션 없이 실행되며, 이미 존재하는 트랜잭션이 있을 경우 예외를 발생시킨다.
  7. NESTED
    이미 존재하는 트랜잭션에 중첩된 형태로 트랜잭션을 시작한다.
    중첩된 트랜잭션은 먼저 시작된 부모 트랜잭션의 커밋과 롤백에는 영향을 받지만
    자신의 
    커밋과 롤백은 부모 트랜잭션에게 영향을 주지 않는다
    메인 트랜잭션이 롤백되면 중첩된 로그 트랜잭션도 같이 롤백되지만
    반대로 중첩된 로그 트랜잭션이 롤백돼도 메인 작업에 이상이 없다면
    메인 트랜잭션은 정상적으로 
    커밋된다.
    주로 중첩 트랜잭션을 지원하는 데이터베이스에서 사용된다.

@Transactional 의 propagation 속성을 이용하면 설정 가능하다.

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class UserService {
    ...
}

 

👣 격리 수준

트랜잭션 격리 수준에 대한 개념적 설명은 아래 게시물에서 수행함.

 

트랜잭션 격리 수준

👣 개요 ACID 원칙 중 Isolation 원칙을 지키기 위해선 트랜잭션 연산 결과가 다른 트랜잭션에 영향을 끼치지 않아야 한다. 이를 완벽하게 지키기 위해선 모든 트랜잭션 연산을 직렬적으로 수행되

ikadnorth.tistory.com

@Transactional 의 isolation 속성을 이용하면 설정 가능하다.

@Service
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public class UserService {
    ...
}

 

👣 제한 시간

트랜잭션 시간 제한(timeout = 10)을 정의하면 트랜잭션이 주어진 시간 내에 완료되어야 하며 
그렇지 않으면 트랜잭션을 롤백시키며 트랜잭션 예외(트랜잭션 시간 만료 오류)가 발생한다.

시간 값 유형은 정수여야 하며 밀리초 단위로 간주된다.

@Transactional 의 timeout 속성을 이용하면 설정 가능하다.

@Service
@Transactional(timeout = 10)
public class UserService {
    ...
}

 

👣 읽기 전용

@Transactional에 readOnly = true 옵션을 주면 
스프링 프레임워크가 세션 flush 모드를 MANUAL로 설정하며 
강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않게 되어서,
트랜잭션이 커밋되면서 실수로 엔티티가 등록, 수정, 삭제되는 일을 방지
할 수 있다.

실수로 인한 수정 방지
트랜잭션이 읽기 전용으로 표시되면 해당 트랜잭션 내에서 엔티티를 수정하려고 시도하면 예외가 발생한다.
이렇게 하면 실수로 엔티티를 수정하는 것을 방지하고 데이터의 일관성을 유지할 수 있다.

성능 향상
트랜잭션이 읽기 전용으로 표시되면 JPA는 엔티티의 스냅샷을 저장하거나 변경 감지를 수행할 필요가 없다.
따라서 특히 많은 엔티티에 액세스하지만 수정되는 엔티티는 거의 없는 상황에서 상당한 성능 향상을 가져올 수 있다.

@Service
@Transactional(readonly = true)
public class UserService {
    ...
}

 

👣 롤백 정책

롤백을 수행할 때, 특정 예외에서만 혹은 특정 예외를 제외한 상황에서만 Rollback을 수행토록할 수 있다.

@Transactional 의 rollbackFor, noRollbackFor 속성을 이용하면 설정 가능하다.

@Service
public class UserService {  

    @Transactional(rollbackFor = { SQLException.class })  
    public void updateUser(User user) throws SQLException {  
        ...
    }  
  
    @Transactional(noRollbackFor = { NullPointerException.class })  
    public User getUserById(Long id) throws NullPointerException {  
        ...
    }  
}

 

'Spring Boot' 카테고리의 다른 글

Spring Boot 전역 예외 처리  (0) 2023.07.30
Lombok  (0) 2023.07.29
SpEL - Spring Expression Language  (0) 2023.07.29
Spring Boot AOP  (0) 2023.07.29
Spring Boot Bean 등록 방법 5가지  (0) 2023.07.29