@Transactional

: @Transactional 어노테이션을 사용하면 tx.begin(), tx.commit()을 자동으로 수행해주고, 예외 발생시 rollback처리를 자동으로 해 주는 편리한 기능이다.

public void register(Member member) {
    try {
      tx.begin();
      memberRepository.save(member);
      tx.commit();
    } catch (Exception e) {
      tx.rollback();
    }
}

와 같은 메소드는 @Transactional 어노테이션 사용 시

@Transactional
public void register(Member member) {
    memberRepository.save(member);
}

로 바꿀 수 있다.

 

내부 구현

: Spring은 프록시 객체를 사용해 위와 같이 tx.being()등의 코드를 삽입해 줍니다. 프록시 객체는 다음과 같이 생성되는데

public class MemberServiceProxy {
  private final MemberService memberService;
  private final TransactonManager manager = TransactionManager.getInstance();

  public MemberServiceProxy(MemberService memberService) {
    this.memberService = memberService;
  }

  public void register(Member member) {
    try {
      manager.begin();
      memberService.register(member);
      manager.commit();
    } catch (Exception e) {
      manager.rollback();
    }
  }
}

개발자가 정의한 메소드를 한번 감싼 뒤, 해당 프록시 메소드를 호출함으로써 이루어집니다.

여기서 발생할 수 있는 문제점과 같이 더욱 자세한 내용은 Spring @Transactional 사용시 주의해야할 점를 참고해 주세요.



사용할 수 있는 Options

 

Propagation

: @Transactional 어노테이션이 붙여진 메서드는 하나의 트랜잭션 안에서 진행될 수 있도록 만들어주는 역할을 한다. 그런데 트랜잭션 안에서 트랜잭션이 호출된다면 어떻게 처리할지를 결정하는것이 propagation 옵션이다. @Transactional(propagation = Propagation.NOT_SUPPORTED)와 같은 방식으로 사용할 수 있다.

옵션 설명
REQUIRED - 기본 옵션
- 부모 트랜잭션이 존재한다면 부모 트랜잭션에 합류, 그렇지 않다면 새로운 트랜잭션을 만든다.
- 중간에 자식/부모에서 rollback이 발생된다면 자식과 부모 모두 rollback 한다.
REQUIRES_NEW - 무조건 새로운 트랜잭션을 만든다.
- nested한 방식으로 메소드 호출이 이루어지더라도 rollback은 각각 이루어 진다.
MANDATORY - 무조건 부모 트랜잭션에 합류시킨다.
- 부모 트랜잭션이 존재하지 않는다면 예외를 발생시킨다.
NESTED - 부모 트랜잭션이 존재하면 부모 트랜잭션에 중첩시키고, 부모 트랜잭션이 존재하지 않는다면 새로운 트랜잭션을 생성한다.
- 부모 트랜잭션에 예외가 발생하면 자식 트랜잭션도 rollback한다.
- 자식 트랜잭션에 예외가 발생하더라도 부모 트랜잭션은 rollback하지 않는다. 이때 롤백은 부모 트랜잭션에서 자식 트랜잭션을 호출하는 지점까지만 롤백된다. 이후 부모 트랜잭션에서 문제가 없으면 부모 트랜잭션은 끝까지 commit 된다.
NEVER - 트랜잭션을 생성하지 않는다. 만약 부모 트랜잭션이 존재한다면 예외를 발생시킨다.


 

Isolation

: 격리수준을 정하는 옵션으로, 트랜잭션 내에서 일관성 없는 데이터를 허용하도록 하는 수준을 정한다. Spring에서는 기본적으로 READ_COMMITED를 사용하는데 Persistence Context를 사용함으로써 REPEATABLE_READ가 보장된다. 다음과 같이 @Transactional(isolation = Isolation.READ_COMMITTED) 사용할 수 있다.

옵션 설명
READ_UNCOMMITED - 트랜잭션에서 commit되지않은 데이터를 다른 트랜잭션에서 읽는 것을 허요한다.
- Dirty Read가 발생한다.
READ_COMMITED - 트랜잭션에서 commit되어 확정된 데이터만을 읽는 것을 허용한다.
REPEATABLE_READ - 한 트랜잭션 내에서 수차례 SELECT를 수행하더라도 동일한 값이 읽혀지는 것을 보장한다.
- Phantom read(새로운 row가 추가/삭제되어 새로 입력된 데이터를 읽는/사라지는 현상)는 여전히 발생 가능하다.
SERIALIZABLE - 모든 작업을 하나의 트랜잭션에 처리하는 것과 같은 높은 고립수준을 제공하는데, 이로인해 동시성 처리 효율은 매우 떨어진다.

여기서 발생되는 Row Lock과 같은 내용은 Lock으로 이해하는 Transaction의 Isolation Level을 참고해 주세요.



rollbackFor

  • 특정 예외가 발생할 경우 rollback하는 것을 명시하는 옵션이다. Spring은 기본적으로 RuntimeException과 Error를 롤백 정책으로 가진다.
  • 사용은 @Transactional(rollbackFor = {RuntimeException.class, Error.class}) 과 같이 할 수 있다. 만약 모든 예외에 대해 rollback하고싶다면 Exception.class를 추가하면 된다.
  • 이와 반대로 특정 Exception에 rollback하고 싶지 않다면 noRollbackFor 옵션을 사용하자.


Read-only

  • 트랜잭션을 읽기 전용으로 설정할 수 있다.
  • 이 옵션이 적용된 트랜잭션은 UPDATE, INSERT, DELETE등과같은 쓰기 작업이 진행되면 예외를 발생시킨다.
  • 이 옵션이 적용되지 않은 트랜잭션은 영속성 컨텍스트에 스냅샷을 만들어두고 트랜잭션이 끝날 때 스냅샷과 Entity의 값을 비교해 쓰기지연 SQL 저장소에 UPDATE 쿼리를 생성해 DB에 flush하는 작업을 거치는데, 이러한 과정이 사라지게 되어 효율적인 작업이 수행될 수 있다.
  • @Transactional(readOnly=true)로 사용할 수 있다.

timeout

  • 지정한 시간 내에 해당 메소드가 수행 완료되지 않으면 rollback을 수행한다.
  • @Transactional(timeout=10)와 같이 사용가능하다. (단위는 sec) (default -1로 no-timeout)

'Backend > SpringBoot-JPA' 카테고리의 다른 글

2차 캐시 - @Cacheable  (0) 2021.04.30
@RestController 어노테이션  (0) 2021.04.29
Optimistic/Pessimistic Lock  (1) 2021.04.19

+ Recent posts