@Transactional
스프링에서 제공하는 선언적 트랜잭션 관리를 위한 어노테이션
AOP를 기반으로 동작하며, 메서드 시작 전에 트랜잭션을 시작하고 메서드 종료 시 커밋 또는 롤백을 수행
클래스나 메서드 레벨에 적용할 수 있다.
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
timeout = -1,
readOnly = false,
rollbackFor = Exception.class,
noRollbackFor = RuntimeException.class
)
주요 속성
- propagation : 트랜잭션 전파 레벨
- isolation : 트랜잭션 격리 수준
- timeout: 트랜잭션 수행 시간 제한 (초 단위)
- readOnly: 읽기 전용 트랜잭션 여부
- rollbackFor: 롤백을 수행할 예외 클래스 지정
- 모든 RuntimeException과 Error에 대해서는 자동으로 롤백
- 체크예외는 자동으로 롤백하지 않기 때문에 추가로 롤백할 예외를 지정하는 것
public class InsufficientBalanceException extends Exception {
}
@Service
public class BankingService {
@Transactional(rollbackFor = InsufficientBalanceException.class)
public void withdraw(long accountId, BigDecimal amount)
throws InsufficientBalanceException {
if (balance.compareTo(amount) < 0) {
throw new InsufficientBalanceException();
}
}
}
- noRollbackFor: 롤백을 수행하지 않을 예외 클래스 지정
주의사항
- private 메서드에는 @Transactional이 동작하지 않는다.
- 같은 클래스 내부 메서드 호출 시에는 프록시가 동작하지 않아 트랜잭션이 적용되지 않을 수 있다.
- RuntimeException과 Error는 기본적으로 롤백을 수행하고, 체크 예외는 롤백하지 않는다.
트랜잭션 전파(Propagation)
propagation 속성을 통해 트랜잭션 동작 방식을 정의할 수 있다.
REQUIRED (기본값)
- 현재 트랜잭션이 있으면 그것을 사용하고, 없으면 새로 생성
- 가장 많이 사용되는 기본적인 전파 방식
REQUIRES_NEW
- 항상 새로운 트랜잭션을 생성
- 기존 트랜잭션이 있다면 잠시 중단시키고 새 트랜잭션을 실행
- 다음과 같은 상황에 주로 사용
- 실패해도 로그는 반드시 남겨야 하는 경우
- 메인 트랜잭션과 별개로 항상 커밋되어야 하는 작업이 있는 경우
SUPPORTS
- 현재 트랜잭션이 있으면 그것을 사용하고, 없어도 그냥 진행
- 트랜잭션이 꼭 필요하지 않은 조회 작업 등에 사용
MANDATORY
- 반드시 기존 트랜잭션이 있어야 한다. 없으면 예외가 발생한다.
NEVER
- 트랜잭션을 사용하지 않는다. 있으면 예외가 발생한다.
NESTED
- 기존 트랜잭션이 있으면 중첩 트랜잭션을 생성
- 중첩 트랜잭션은 부모 트랜잭션의 커밋/롤백에 영향을 받지만, 자신의 롤백은 부모에게 영향을 주지 않는다.
트랜잭션 격리(Isolation) 수준
DEFAULT
- DB의 기본 격리 수준을 사용
- 대부분의 DB는 READ_COMMITTED를 기본값으로 사용. mysql은 repeatable
READ_UNCOMMITTED
- 가장 낮은 격리 수준
- Dirty Read, Non-Repeatable Read, Phantom Read 현상이 발생할 수 있음
- 성능은 가장 좋지만 데이터 정합성이 깨질 수 있음
READ_COMMITTED
- Dirty Read를 방지
- Non-Repeatable Read와 Phantom Read는 여전히 발생 가능
- 일반적으로 가장 많이 사용되는 격리 수준
REPEATABLE_READ
- Dirty Read, Non-Repeatable Read를 방지
- Phantom Read는 여전히 발생 가능
- 같은 데이터를 여러 번 조회해도 동일한 결과를 보장
SERIALIZABLE
- 가장 높은 격리 수준
- 모든 트랜잭션을 순차적으로 실행
- 완벽한 데이터 정합성을 보장하지만 성능이 가장 떨어진다.