본문 바로가기

Spring Boot/JPA

@Transactional method의 Transaction 전파

공부를 하다가 @Transactional method는 어떻게 전파되는지 궁금해졌다.

그리고 결국 정말 오랜 시간을 박치기해서 내가 원하는 결론에 다다를 수 있었다.
다음 결론들은 기본 전파 전략인 Required를 사용했을 때의 동작이다.
다른 전파 전략을 원하는 사람들은 공식문서를 참고해보자!.

결론은 다음과 같다.

맨 처음에 호출된 @Transactional method의 Transaction 설정이 그대로
다른 method들에게 전파된다는 것이다.
(도대체 이 결론을 얻기 위해 얼마나 많이... 시도하고 찾아봤는지 모르겠다.)


@Transactional method에서 Non-@Transactional method 호출

MemberJpaRepositoryTest.java

package study.datajpapractice.repository;

//import 생략

@Rollback(false)
@SpringBootTest
class MemberJpaRepositoryTest {

    @Autowired MemberJpaRepository memberJpaRepository;
    @PersistenceContext
    EntityManager em;

    @Test
    @Transactional(readOnly = false)
    public void transactionTest() {
        String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
        boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();

        System.out.println("currentTransactionName1 = " + currentTransactionName);
        System.out.println("currentTransactionIsActive1 = " + isActive);
        memberJpaRepository.noTrMethod();
    }
}

 

MemberJpaRepository.java

package study.datajpapractice.repository;

// import 생략 

public class MemberJpaRepository {
    @PersistenceContext
    private EntityManager em;
    
    public void noTrMethod() {
        String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
        boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
        System.out.println("currentTransactionName2 = " + currentTransactionName);
        System.out.println("currentTransactionIsActive2 = " + isActive);
        noTrMethod2();
    }

    public void noTrMethod2() {
        String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
        boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
        System.out.println("currentTransactionName3 = " + currentTransactionName);
        System.out.println("currentTransactionIsActive3 = " + isActive);
    }
}

 

코드의 흐름은 다음과 같다.

transactionTest > noTrMethod > noTrMethod2 method 순으로 호출된다.

또한 transactionTest@Transactional method이고,
나머지 2개의 method는 Non-@Transactional method이다.

 

그러면 @Transaction method에서 Non-@Transactional method를 호출하면 Transaction이 끊길까?
정답은 '끊기지 않는다'이다.

@Transactional method에서 생성된 Transaction
Non-@Transactional method에 그대로 전파된다.

Transaction 이름과 활성화여부를 출력하는 위 코드들을 실행해서 확인해보자!

3개의 method에서 transaction명이 모두 똑같이 나오고,

Transaction 활성화여부도 true로 나오는 것을 알 수 있다.

즉, transactionTest를 호출할 때 만들어진 Transaction이 그대로 전파된 것이다.

아무래도 이렇게 Transaction 이름과 활성화여부만 출력해봐서는
진짜 Transaction이 잘 작동하는지 모르겠다면 noTrMethod2를 다음과 같이 수정해보자!

 

public void noTrMethod2() {
    String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
    boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
    System.out.println("currentTransactionName3 = " + currentTransactionName);
    System.out.println("currentTransactionIsActive3 = " + isActive);

    Member findMember = findById(1L).orElse(null);
    findMember.updateUsername(UUID.randomUUID().toString());
}

 

(추가된 코드는 단순히 Member Entity를 DB에서 조회해서

randomUUID값으로 username을 수정하는 코드이다.)

noTrMethod2를 실행했을 때 변경감지에 의해 update 쿼리가 실행된다면,
Transaction이 정상작동하고 있다는 것을 좀 더 실감할 수 있을 것이다.

 

실행결과를 보면 변경감지에 의해 update 쿼리가 정상적으로 실행되었음을 알 수 있다.

또한 transactionTest method에서 @Transactional을 제거하면
Transaction이 생성 및 전파되지 않아 update 쿼리가 실행되지 않는다.

 


@Transactional method에서 @Transactional method 호출

(이 주제에 대해서는 코드를 직접 짜보지 않을 거다.
난 이미 직접 짜서 확인해보았고, 이 부분까지 코드로 정리하기 너무 힘들다 ㅠㅠㅠ.

다만 미래의 나를 위해 정리는 해놓는다.)

 

@Transactional method에서 @Transactional method를 호출하면 어떻게 될까?

맨 처음 @Transactional method가 호출될 때
생성된 Transaction이 내부 method로 전파된다.

이 부분을 나는 @TransactionalreadOnly parameter를 다르게 해가며,

디버깅모드에서 EntityEntry 값을 확인해보며 체크했다.

사실 굳이 이렇게 안 해도 currentTransactionName만 찍어봐도
맨 처음 생성된 Transaction을 계속 사용하는 것을 확인할 수 있을 것이다.

 

또한 이 부분은 공식문서를 보면 알 수 있다. (Required 전파전략 참고)

 

혹시라도 잘못된 부분이 있으면, 댓글 달아주면 감사하겠습니다!

'Spring Boot > JPA' 카테고리의 다른 글

@Transactional(readOnly = false)일 때 얻는 이점  (0) 2022.02.09
Spring Boot + JPA 설정  (0) 2021.11.02