@Transactional은 Fallback 정책을 통해서 특정 트랜잭션 속성을 적용할 대상을 매우 유연하게 선정할 수 있도록 하는 어노테이션이다. Fallback 정책은 @Transactional 어노테이션의 작성 위치에 따른 설정의 우선순위를 다르게 적용하는 것이다.
타깃 메소드 > 타깃 클래스 > 선언 메소드 > 선언 타입
※ 여기에서 선언은 인터페이스처럼 선언만 하고 구현하지 않은 대상을 뜻한다.
트랜잭션의 구조 및 속성은 다음과 같다.
@Target({ElementType.TYPE, ElementType.METHOD}) // 어노테이션 사용대상 지정
@Retention(RetentionPolicy.RUNTIME) // 어노테이션의 정보유지 기간
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
String[] label() default {};
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
String timeoutString() default "";
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
일부 트랜잭션 매니저에서는 일부 속성을 지원하지 않을 수 있기 때문에, 트랜잭션의 속성은 각 트랜잭션 매니저에 대해 잘 공부하고 사용하는 것을 권장한다.
이제 각 속성이 의미하는 바와 지정할 수 있는 속성값에는 무엇이 있는지 살펴보자.
1. propagation
- 트랜잭션의 전파 속성을 의미한다. org.springframework.transaction.annotation.Propagation에 정의된 이늄 값을 사용하며, 기본값은 Propagation.REQUIRED이다.
(1) Propagation.REQUIRED
- 기본값 속성으로, 보통은 이 속성으로 충분하다.
- 진행 중인 트랜잭션이 있으면 이에 참여하고 그렇지 않으면 트랜잭션을 새로 시작한다.
- 하나의 트랜잭션이라도 문제가 발생하면 전체를 rollback 한다.
(2) Propagation.SUPPORTS
- 진행 중인 트랜잭션이 있으면 이에 참여하고 그렇지 않으면 트랜잭션 없이 독립적으로 실행한다.
- 트랜잭션이 없더라도 해당 경계 안에서 Connection이나 Hibernate Session 등을 공유할 수 있다.
(3) Propagation.MANDATORY
- 진행 중인 트랜잭션이 있으면 이에 참여하고 그렇지 않으면 예외를 발생시킨다.
- 독립적으로 트랜잭션을 진행하면 안되는 경우에 사용할 수 있다.
(4) Propagation.REQUIRED_NEW
- 항상 새로운 트랜잭션을 시작한다.
- 진행 중인 트랜잭션이 있으면 보류한다.
(5) Propagation.NOT_SUPPORTED
- 트랜잭션을 적용하지 않는다.
- 진행 중인 트랜잭션이 있으면 보류한다.
(6) Propagation.MANDATORY
- 트랜잭션을 적용하지 않는다.
- 진행 중인 트랜잭션이 있다면 예외를 발생시킨다.
(7) Propagation.NESTED
- 이미 진행 중인 트랜잭션이 있으면 중첩 트랜잭션을 시작한다. 즉, 트랜잭션 안에 다시 트랜잭션이 만들어진다.
- 중첩된 트랜잭션은 부모 트랜잭션에 영향을 받지만 부모 트랜잭션에 영향을 주진 않는다.
- 작업이 실패하더라도 메인 작업의 트랜잭션까지 롤백할 필요는 없을 때 사용할 수 있다.
- 중첩 트랜잭션을 지원하는 드라이버, 트랜잭션 매니저를 이용할 때 적용 가능하다.
2. isolation
- 트랜잭션의 격리 수준이다. 동시에 여러 트랜잭션이 진행될 때 트랜잭션 작업 결과를 다른 트랜잭션에게 어떻게 노출할지를 결정하는 속성이다.
- org.springframework.transaction.annotation.Isolation에 정의된 이늄값을 사용하며, 기본값은 Isolation.DEFAULT이다.
1) DEFAULT
- DB의 기본 격리 수준을 따른다. 대부분의 DB는 READ_COMMITTED를 기본 격리 수준으로 갖는다.
2) READ_UNCOMMITTED
- 가장 낮은 격리 수준으로, 하나의 트랜잭션이 커밋되기 전에 그 변화가 다른 트랜잭션에 노출된다.
※ 이처럼 하나의 트랜잭션이 커밋되기 전에 그 변화가 다른 트랜잭션에 노출되는 문제를 Dirty Read라고 한다.
- 데이터의 일관성은 떨어지더라도 속도는 매우 빠르다.
3) READ_COMMITTED
- 하나의 트랜잭션이 아직 커밋되지 않았다면 다른 트랜잭션에 그 변화가 노출되지 않는다.
- 하나의 트랜잭션이 읽은 row가 도중에 커밋한 다른 트랜잭션에 의해 수정될 수 있다.
※ 이처럼 같은 행을 두 번 이상 조회했을 때 값이 달라지는 문제를 Non-repeatable Read라고 한다.
- 실제로 가장 많이 사용되는 격리 수준이다.
4) REPEATABLE_READ
- 하나의 트랜잭션이 읽은 row가 도중에 다른 트랜잭션에 의해 수정되는 것을 막아준다.
- 새로운 row가 추가되는 것 까지는 막지 못한다.
※ 이처럼 반복 조회 시 결과 집합이 달라지는 것을 Phantom Read라고 한다.
5) SERIALIZABLE
- 가장 강력한 격리 수준으로, 트랜잭션을 순차적으로 진행시킨다. 즉, 여러 트랜잭션이 동시에 같은 테이블에 접근하지 못하게 막는다.
- 가장 안전하지만 성능이 떨어진다.
3. timeout
- 트랜잭션에 제한시간을 지정하는 속성이다. 단위는 초(s)로, 보통은 트랜잭션 시스템의 제한 시간을 따른다.
4. readOnly
- 트랜잭션을 읽기전용으로 설정하는 속성으로, 기본값은 false다.
- 쓰기 작업(INSERT, UPDATE, DELETE)을 방지할 수 있고, 성능도 좋아진다.
- 보통 get, find로 시작하는 메소드에 적용된다.
5. rollbackFor, rollbackForClassName
- 체크 예외지만 rollback 대상으로 삼아야 하는 예외를 지정하는 속성이다.
※ 선언적 트랜잭션에서는 런타임 예외가 발생하면 rollback하고, 체크 예외가 발생하면 commit한다는 특징이 있다. Spring에서는 데이터 엑세스 기술에 대한 예외를 런타임 예외로 전환해주기 때문에 런타임 예외를 rollback 하는 것이다.
- rollbackFor에는 예외 클래스(.class)를 지정하고, rollbackForClassName에는 예외 클래스의 이름을 지정한다.
6. noRollbackFor, noRollbackForClassName
- 런타임 예외지만 commit 대상으로 삼아야 하는 예외를 지정하는 속성이다.
- noRollbackFor에는 예외 클래스(.class)를 지정하고, noRollbackForClassName에는 예외 클래스의 이름을 지정한다.
● 참고 자료 : 토비의 스프링 3.1 Vol.2
'Spring Series > Spring Framework' 카테고리의 다른 글
[Spring] 웹 프레젠테이션 계층 테스트 (0) | 2022.07.14 |
---|---|
[Spring] DispatcherServlet의 DI 전략 (0) | 2022.07.13 |
[Spring] Hibernate (0) | 2022.07.06 |
[Spring] Spring이 제공하는 JPA (0) | 2022.07.02 |
[Spring] 더 편리해진 Spring JDBC (0) | 2022.06.29 |
댓글