6.7.1 트랜잭션 애노테이션
- 포인트컷과 트랜잭션 속성을 이용해 트랜잭션을 일괄적으로 적용하는 방식은 대부분 상황에서 잘 들어맞음
- 하지만, 세밀하게 튜닝된 트랜잭션 속성의 경우 포인트컷과 트랜잭션 속성 사용으로는 적합하지 않음
- 포인트컷과 트랜잭션 속성과 같이 설정 파일에서 분류 가능한 그룹으로 만들어 일괄적으로 속성을 부여하는 대신, 직접 타킷에 속성정보를 가진 애노테이션을 지정해 세밀하게 트랜잭션 속성을 적용함
- 이 애노테이션을 트랜잭션 애노테이션이라 함
- @Transaction 애노테이션을 정의한 코드
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transaction {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
- @Transaction 타깃은 메소드와 타입임
- 따라서 메소드, 클래스, 인터페이스에 사용할 수 있음
- @Transaction이 부여된 모든 오브젝트를 자동 타깃 오브젝트로 인식함
- 이때, 사용되는 포인트컷은 TransactionAttributeSourcePointcut임
- TransactionAttributeSourcePointcut은 스스로 표현식과 같은 선정 기준을 갖지 않음
- 대신, @Transaction이 부여된 메소드 및 오브젝트를 모두 찾아 포인트컷 선정 결과로 돌려줌
- 즉, 포인트컷을 통과시켜 트랜잭션 속성을 이용해 선정 메소드 및 타입을 필터링함
- @Transaction 엘리먼트로 트랜잭션 속성을 부여함
- 예시
@Transactional(readOnly=ture)
6.7.2 트랜잭션 속성을 이용하는 포인트컷
- TransactionInterceptor는 메소드 이름 패턴을 통해 부여되는 일괄적인 트랜잭션 속성정보 대신 @Transaction 엘리먼트에서 트랜잭션 속성을 가져오는 AnnotationTransactionAttributeSource를 사용함
- @Transaction은 메소드 및 타입마다 다르게 설정할 수 있어 매우 유연한 트랜잭션 속성 설정이 가능함
- @Transaction 방식을 사용하면 포인트컷과 트랜잭션 속성을 애노테이션 하나로 지정할 수 있음
- 단 메소드마다 @Transaction을 넣어 유연한 속성 제어는 가능하겠지만, 코드가 지저분해지고 동일한 속성 정보를 가진 애노테이션을 반복적으로 사용할 수 밖에 없음
6.7.3 대체 정책
- @Transactional을 적용할 때 4단계의 대체(Fallback)정책을 사용하여 반복적인 애노테이션 사용을 막기을 수 있음
- @Transaction 애노테이션을 찾을 때 순서가 있음
- 타깃 메소드
- 타깃 클래스
- 선언 메소드
- 선언 타입
[1]
public interface Service {
[2]
void method1();
[3]
void method2();
}
[4]
public class Servicelmpl implements Service {
[5]
public void method1() (
[6]
public void method2() {
}
- @Transaction 애노테이션을 찾는 순서에 따라, 타깃 메소드인 [5]와 [6]이 @Transaction이 올 수 있는 첫 번째 후보임. 두 번째는 타깃 클래스인 [4]임. 세 번째는 선언 메소드인 [2]와 [3]임. 네 번째는 선언 타입인 [1]임
- 타깃 클래스나, 선언 타입에만 @Transactional을 넣으면 해당 클래스 및 타입에 있는 메소드는 공통적으로 클래스 및 선언 레벨에 있는 @Transaction 엘리먼트를 공유함
- 인터페이스에 @Transaction을 두면 구현 클래스가 바뀌더라도 트랜잭션 속성을 유지할 수 있다는 장점이 있음
- 하지만, 프록시 방식이 아닌 트랜잭션을 적용하면 인터페이스의 @Transaction은 무시되기 때문에, 타깃 클래스에 @Transaction을 넣는 방식을 권장함
6.7.4 트랜잭션 애노테이션 적용
- 스프링은 @Transactional을 이용한 트랜잭션 속성을 사용하는데 필요한 설정을 태그 하나에 담아뒀음
<beans
...>
<tx:annotaion-driven>
</beans>
6.7.5 UserService에 @Transactional 적용
- 꼭 세밀한 트랜잭션 설정이 필요할 때만 @Transactional을 사용해야 하는 것은 아님
- @Transactional 사용은 포인트컷과 트랜잭션 속성 지정하는 것보다 단순하고 직관적이기 때문에 @Transactional을 주로 사용하기도 함
- 하지만, @Transactional을 빼 먹어도 컴파일에 아무런 문제가 없고 롤백이 발생했을 때 빼 먹은 사실을 알 수 있기 때문에 매우 조심해서 사용해야 함
- 아래 xml 파일 설정을 @Transactional로 쉽게 바꿀 수 있음
<beans
...>
...
<tx:advice id="transactionAdvice">
<tx:attributes>
<!--propagation이 "REQUIRED"일 경우 생략 가능-->
<tx:method name="get*" read-only="true"/>
<tx:method name="*" />
</tx:attributes>
</tx:advice>
</beans>
- UserServiceImpl와 TestService 두 개의 클래스에 적용하기 위해 인터페이스인 UserService에서 @Transactional 사용
@Transactional
public interface UserService {
//<tx:method name="*" />와 같음
void add(User user);
void deleteAll();
void update(User user1);
void upgradeLevels();
//<tx:method name="get*" read-only="true"/>와 같음
@Transactional(readOnly = true)
User get(String id);
@Transactional(readOnly = true)
List<User> getAll();
}
'토비의 스프링 정리' 카테고리의 다른 글
토비의 스프링 - 6.9 6장 정리 (0) | 2022.10.20 |
---|---|
토비의 스프링 - 6.8 트랜잭션 지원 테스트 (0) | 2022.10.20 |
토비의 스프링 - 6.6 트랜잭션 속성 (0) | 2022.10.20 |
토비의 스프링 - 6.5 스프링의 AOP (0) | 2022.10.20 |
토비의 스프링 - 6.4 스프링의 프록시 팩토리 빈 (0) | 2022.10.07 |