영호

[Spring] @Transactional 의 REQUIRED, REQUIRES_NEW 트랜잭션 생성 과정 본문

Spring

[Spring] @Transactional 의 REQUIRED, REQUIRES_NEW 트랜잭션 생성 과정

0h0 2023. 10. 14. 23:25

들어가면서,

트랜잭션 전파 옵션에는 REQUIRED, REQUIRES_NEW 등등이 있다. 그 중에서 이번에는 REQUIRED, REQUIRES_NEW 옵션일 때 트랜잭션 생성 과정에 대해 알아볼 예정이다.

  • REQUIRED 는 어떻게 기존 트랜잭션이 존재하면서 해당 트랜잭션에 참여하고, 없으면 새로운 트랜잭션을 만들까?
  • REQUIRES_NEW 는 어떻게 매번 새로운 트랜잭션을 만들까?

이 부분에 대해 알아볼 때 핵심적인 기능을 하는 클래스는 AbstractPlatformTransactionManager, JpaTransactionManager, TransactionSynchronizationManager 가 있다.

이 클래스들이 기존 트랜잭션이 있는지 확인한다. 없으면 새로운 트랜잭션 설정을 해주고, 기존 트랜잭션이 있으면 REQUIRED, REQUIRES_NEW 옵션에 맞게 트랜잭션 설정을 해준다.

REQUIRED

[REQUIRED] 첫 트랜잭션 생성 과정

처음 트랜잭션이 생성되는 경우를 우선 살펴보자. 우선 트랜잭션 관련 인스턴스를 생성하고 여기에 트랜잭션 관련 리소스를 할당해주는 과정을 거친다. 아래는 트랜잭션 생성 시 초반에 호출되는 과정이다.

 

눈 여겨 봐야할 부분은 JpaTransactionManager 에서 emHolder 가 null 이 반환되고, txObject 의 entityManagerHolder 도 null 인 상태로 반환된다는 것이다. newEntityManagerHolder 의 경우 다른 코드가 호출되면서 true 로 바뀌게 된다.

이렇게 만들어진 인스턴스를 토대로 기존에 트랜잭션이 존재하는지 확인 하는데 그 과정은 아래 코드를 통해 이루어진다.

 

entityManagerHolder 가 null 이기 때문에 기존에 존재하던 트랜잭션이 없다고 판단한다. 이후 AbstractPlatformTransactionManager.startTransaction() 이 실행되면서 트랜잭션 생성이 마무리 된다.

그리고, 이 과정에서 TransactionSynchronizationManager.bindResource() 를 호출한다. 그래서 이후 생성되는 트랜잭션은 TransactionSynchronizationManager 에 관련 자원이 있기 때문에 기존 트랜잭션이 존재한다고 판단된다.

 


[REQUIRED] 기존에 트랜잭션이 존재하는 경우

첫 트랜잭션이 생성되는 과정과는 다르게 이번엔 TransactionSynchronizationManager 에 값이 있기 때문에 트랜잭션 관련 인스턴스의 entityManagerHolder가 null 아니라는 것이다.

 

이로 인해 AbstractPlatformTransactionManager.isExistringTransaction() 이 true 가 되어 this.handleExistingTransaction() 이 호출된다.

 

아래 캡쳐본에서 JpaTransactionManager 의 박스친 부분을 보면 newTransaction = false 로 설정되는 것을 볼 수 있다. 즉, 현재 만들어지는 트랜잭션은 새로운 트랜잭션이 아니라는 것이다. 이는 나중에 COMMIT 에 영향을 미친다.

 

이 과정에서 TransactionSynchronizationManager.bindResource() 가 호출되지 않는다.

REQUIRES_NEW

REQUIRES_NEW 는 기존에 트랜잭션이 존재하더라도 새롭게 트랜잭션을 생성한다. REQUIRES_NEW 옵션으로 처음 트랜잭션을 생성하는 것과 REQUIRED 옵션으로 생성하는 것과 과정은 동일하기 때문에 바로 기존 트랜잭션이 존재하는 경우에 대해 알아보자.

 

REQUIRES_NEW 옵션이라도 트랜잭션이 존재하는 상황에서는 isExistingTransaction() == true 이다. 그래서 handleExistingTransaction() 이 호출되는데, 해당 메서드 실행 과정이 REQUIRED 와 다르다.

REQUIRED 는 handleExistingTransaction()else 블록이 실행됐지만 REQUIRES_NEW다른 블록이 실행된다. 다른 블록이 실행되는 이유는 definition 필드가 다르기 때문이다. definition 에는 propagation, isolation 정보가 있다.

 

그리고 한 가지 더 주목할 점은 첫 트랜잭션을 생성할 때 실행되는 startTransaction() 이 실행된다는 것이다.

 

이를 통해, REQUIRES_NEW 로 만들어진 트랜잭션은 newTransaction 속성이 true 로 만들어지고, doBegin() 을 통해 해당 트랜잭션 관련 리소스도 TransactionSynchronizationManager에 저장된다. 물론 기존 값에 대한 처리도 같이 해주고 있다.

결론

디버깅을 통해 REQUIRED 는 어떻게 기존 트랜잭션에 참여하고, REQUIRES_NEW 는 어떻게 매번 새로운 트랜잭션을 만드는지 살펴봤다.

간단하게 결론만 요약하자면 AbstractPlatformTransactionManager.handleExistingTransaction() 메서드의 분기문에 의해 REQUIRED, REQUIRES_NEW 동작이 달라지는 것을 확인할 수 있었다.

 

 

혹시 잘못된 부분 있으면 언제든 댓글 달아주세요~

Comments