@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로 전파된다.
이 부분을 나는 @Transactional의 readOnly parameter를 다르게 해가며,
디버깅모드에서 EntityEntry 값을 확인해보며 체크했다.
사실 굳이 이렇게 안 해도 currentTransactionName만 찍어봐도
맨 처음 생성된 Transaction을 계속 사용하는 것을 확인할 수 있을 것이다.
또한 이 부분은 공식문서를 보면 알 수 있다. (Required 전파전략 참고)
혹시라도 잘못된 부분이 있으면, 댓글 달아주면 감사하겠습니다!