티스토리 뷰

JVM/Spring

[Spring] @Transactional

글을 쓰는 개발자 2022. 4. 2. 22:09
반응형

Spring Transactional

스프링 Transaction 처리는 PlatformTransactionManager를 구현한 클래스를 통해 처리한다.
이를 구현한 클래스는 DataSourcePlatformTransactionManager, HibernatePlatformTransactionManager, JpaPlatformTransactionManager 등 다양하게 존재한다. (여기에서도 느껴지는 스프링의 철학. 역할과 구현을 분리. PlatformTransactionManager라는 역할을 만들고 이를 구현한 클래스를 각 상황에 맞게 사용)

 

예외처리

Transactional 어노테이션을 감싼 메소드에서 RuntimeException의 경우에는 롤백이 일어나고, checkedException(가령 IOException)의 경우에는 커밋이 된다.

 

PlatformTransactionManager 에서 getTransaction

getTransaction은 TransactionStatus을 리턴하는데, TransactionDefinition 파라미터 값에 따라 결정된다. 새로운 트랜잭션을 주거나, 아니면 현재 call stack에 매칭되는 트랜잭션을 줄 수도 있다.

 

TransactionDefinition

TransactionDefinition 인터페이스는 다음과 같은 메소드를 규정하고 있다.

  • Isolation: 트랜잭션의 고립수준의 값을 나타내며, READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE 단계 값을 가지고 있다.
  • Propagation: 현재 진행중인 트랜잭션이 존재할 때 새로운 트랜잭션 메소드를 호출하는 경우 어떤 정책을 사용할 지에 대한 설정값을 말한다.
  • Timeout: 트랜잭션 유지시간을 말한다.
  • Read only: 읽기전용으로 쓸 것인지 아닌 지에 대한 설정 값
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults()); //위 4속성 설정값 객체
if (isExistingTransaction(transaction)) {
            return handleExistingTransaction(def, transaction, debugEnabled); // 다음과 같이 설정값을 같이 전달
        }
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
            ...
        }

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
            ...
        }

다음 위와 같이 설정 값에 따라 어떻게 작동할 것인지 조건문으로 처리하는 모습을 볼 수 있다.

선언적 트랜잭션 관리(Declarative Transaction Management)

트랜잭션은 AOP 프록시를 통해 처리가 된다. 이러한 특성을 롤백 기준 등 다양한 옵션을 선언할 수 있다.

어노테이션 기반으로 처리할 때에는 @Transactional, @EnableTransactionManagement(데이터베이스 관련 설정할 때 사용)을 사용하여 선언적 트랜잭션을 처리할 수 있다.
트랜잭션 AOP를 어떻게 처리하는 지 한 번 살펴보자.
TransactionInterceptor


public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable{
@Override
    @Nullable
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // Work out the target class: may be {@code null}.
        // The TransactionAttributeSource should be passed the target class
        // as well as the method, which may be from an interface.
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

        // Adapt to TransactionAspectSupport's invokeWithinTransaction...
        return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
            @Override
            @Nullable
            public Object proceedWithInvocation() throws Throwable {
                return invocation.proceed();
            }
            @Override
            public Object getTarget() {
                return invocation.getThis();
            }
            @Override
            public Object[] getArguments() {
                return invocation.getArguments();
            }
        });
    }

}

Implement this method to perform extra treatments before and after the invocation

위의 내용은 MethodInterceptor 안에 있는 invoke 메소드 주석내용으로 AOP와 관련된 내용임을 알 수 있다.

트랜잭션은 다음과 같이 작동한다.(간략하게)

요청 -> AOP PROXY -> Transaction Advisor -> Target Method

@Transactional

@Transactional은 오직 public 메소드만 동작한다. protected, private 메소드에 @Transactional 어노테이션을 붙이지 않는다면 아무런 일도 일어나지 않을 것이다.

@Transactional Settings

  • value: qualifier를 설정하는 값(빈의 이름을 설정)
  • propagation: Propagation Enum을 통해 어떤 방식으로 propagation을 설정할 것인지 설정
  • isolation: 고립수준 세팅. default는 각 DB에 맞는 고립수준 설정 (MySQL은 REPEATABLE READ, PostgreSQL은 READ COMMITTED)
  • readOnly: 읽기전용 유무 설정
  • timeout: 트랜잭션 시간 설정
  • rollbackFor: 특정 Exception에서 무조건 rollback
  • noRollbackFor: 특정 Exception에서 무조건 rollback X

value는 어떻게 사용?

예시는 공식문서 내용 참고한다.

public class TransactionalService {

    @Transactional("order")
    public void setSomething(String name) { ... }

    @Transactional("account")
    public void doSomething() { ... }
}
<tx:annotation-driven/>

    <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        ...
        <qualifier value="order"/>
    </bean>

    <bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
        ...
        <qualifier value="account"/>
    </bean>

이렇게 되면 order는 DataSourceTransactionManager를 , account는 JpaTransactionManager를 바라보게 된다.

매번 저렇게 사용하기 귀찮을 때는 커스텀을 하면 된다.

Custom Usage

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("order")
public @interface OrderTx {
}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("account")
public @interface AccountTx {
}

 

트랜잭션 작동 방식

https://deveric.tistory.com/86

반응형

'JVM > Spring' 카테고리의 다른 글

[Hikari CP] 光 살펴보기 - 1  (0) 2022.04.26
[Spring] AOP  (0) 2022.04.05
[JAVA][Spring][dirty checking] 준영속 엔티티 대해서  (0) 2021.12.09
[JPA] JPA N+1 문제  (0) 2021.12.03
[Spring Boot] Slack Bot 연동 작업  (0) 2021.11.01
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함