@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 |