티스토리 뷰
애플리케이션을 개발하다 보면, 보통 @Transactional을 사용해서 Transaction을 사용합니다.
관습적으로 사용하다 보니, 내부적으로 어떻게 돌아가는지 원리에 대해서만 관심을 가졌습니다.
하지만 여기에서는 조금 더 들어가서 살펴보려고 합니다.
Transaction은 두 가지 형태로 구현할 수 있습니다.
- Programmatic Transaction 방식 : 직접 코드에서 Transaction을 구현하는 방식
- Declarative Transaction 방식 : 원하는 Scope에서 Annotation을 선언하는 방식
여기서 이야기하려고 하는 주제는 2.Declarative Transaction방식입니다.
여기서 다시 "2.Declarative Transaction"의 종류는 다음 두 가지로 나뉩니다.
- Annotation으로 Transaction 선언
- AOP Configuration으로 Transaction 선언
"어노테이션(Annotation)으로 트랜잭션 선언"으로 사용하다 보면, 서로 다른 패키지에 담긴 @Transactional을 보게 됩니다. 여기서 궁금증이 생겼습니다.
아주 간혹 레거시에서 구현된 코드를 보면 javax.transaction가 사용된 클래스를 발견할 수 있었기 때문입니다.
우리가 보통 볼 수 있는 @Transactional의 종류는 다음과 같습니다.
- org.springframework.transaction.annotation의 @Transactional
- javax.transaction의 @Transactional
1번과 2번의 패키지의 선언적인 Annotation @Transactional을 어떻게 동작하고, 어떤 걸 사용해야 할까요?
일단, 테스트 환경은 Spring v5.2.6.RELEASE에서 진행하였습니다.
우리가 보통 사용하는 Service Layer안에 있는 method 위에 @Transactional을 적용했습니다.
@javax.transaction.Transactional
void createEmailUser1() {
User user = new User("test@gmail.com", "1234");
userRepository.save(user);
}
@org.springframework.transaction.annotation.Transactional
void createEmailUser2() {
User user = new User("test@gmail.com", "1234");
userRepository.save(user);
}
위 코드를 테스트 실행 했을 때, 정상적으로 user가 저장이 되는 것을 확인할 수 있습니다.
이번에는 Transaction 안에는 Unchecked Exception이 일어났을 때, Rollback이 진행되는지 살펴보겠습니다.
@javax.transaction.Transactional
void createEmailUser1() {
User user = new User("test@gmail.com", "1234");
userRepository.save(user);
throw new RuntimeException();
}
@org.springframework.transaction.annotation.Transactional
void createEmailUser2() {
User user = new User("test@gmail.com", "1234");
userRepository.save(user);
throw new RuntimeException();
}
Rollback과 마찬가지로 의도한 대로 정상적으로 user의 저장은 Unchecked Exception이 발생하면서 Rollback 되었습니다.
여기까지 결론은 Declarative Transaction 방식으로 Transaction을 관리하였을 때,
@Transactional의 종류와 상관없이 Transaction을 관리가 가능하다는 것을 알 수 있었습니다.
그러면 왜 다른 @Transactional을 사용할 수 있게 제공되어있을까?
조금 더 패키지를 면밀하게 보겠습니다.
- 일단 패키지 이름을 보면, 힌트가 있습니다.
- @javax.transaction.Transactional의 javax는 자바 표준 확장 패키지를 뜻합니다. 자바에서 기본 제공
- @org.springframework.transaction.annotation.Transactional는 org.springframework에서 제공, Container가 관리해준다는 것이겠죠!?
- 해당 패키지를 들어가 보면 패키지 구성이 다르다는 것을 알 수 있습니다.
- @javax.transaction.Transactional은 TransactionType, rollback 정도로 구현되어있습니다.
- @org.springframework.transaction.annotation.Transactional은propagation, isolation, timeout, readOnly, rollback으로 구성되어있습니다.
- Java에서 제공하는 Transaction 기능에서 Spring에서 Framework를 위한 기능을 보충하였습니다.
여기까지 보면 @org.springframework.transaction.annotation.Transactional를 Spring에서 사용한다면 많은 옵션으로 구현에 유리할 거라 생각을 할 수 있습니다.
저는 여기서 한 단계 더 들어가 보겠습니다.
저희가 선언해놓은 @Transactional을 어디에서 선언했는지 알고, 해당 Scope의 소스에 Transaction을 적용할지 찾아보았습니다.
@javax.transaction.Transactional를 Parsing 하는 class가 있었습니다.
package org.springframework.transaction.annotation;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
/**
* Strategy implementation for parsing JTA 1.2's {@link javax.transaction.Transactional} annotation.
*
* @author Juergen Hoeller
* @since 4.0
*/
@SuppressWarnings("serial")
public class JtaTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
...
}
일단 package를 보면, springframework에서 @javax.transaction.Transactional를 인식해서 Transaction을 Parsing 합니다.
그리고 @since를 보시면 4.0부터라고 쓰여있으니, Spring 4.0 이상부터@javax.transaction.Transactional를 지원합니다.
그리고 @org.springframework.transaction.annotation.Transactional를 Parsing 하는 class도 있었습니다.
package org.springframework.transaction.annotation;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
/**
* Strategy implementation for parsing Spring's {@link Transactional} annotation.
*
* @author Juergen Hoeller
* @since 2.5
*/
@SuppressWarnings("serial")
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
...
}
@org.springframework.transaction.annotation.Transactional은 @since 2.5라고 쓰여있는 것을 보니 Spring 2.5 이상부터 지원합니다.
"어떤 @Transactional을 사용해야 할까?"의 결론
- Spring에서 @Transactional을 사용할 때, 더 많은 옵션과 더 많은 버전을 지원하는 @org.springframework.transaction.annotation.Transactional를 사용하는 것이 개발에 유리해 보입니다.
- 옵션 사용을 하지 않고, Spring Version도 높다면, 어느 @Transactional을 사용하던 상관없다는 게 여기까지 결론입니다.
'Spring' 카테고리의 다른 글
Spring "Field Injection"? or "Constructor Injection"? (0) | 2020.06.16 |
---|---|
Spring Transaction Exception 상황에서 Rollback 처리하기 (2) | 2019.12.31 |
Junit test case를 순서대로 실행 할 수 있을까? (0) | 2019.08.24 |
트래픽 과부하가 발생할 경우 어떻게 할 것인가? (0) | 2019.01.30 |