본문 바로가기
[개발] 프레임워크/Spring

트랜잭션 동일성 비교하기

by Devsong26 2024. 2. 8.

비즈니스 메서드를 리팩터링 하는 와중에 아래와 같은 고민이 생겼습니다.

@Trasactional(propagtaion=Propagation.REQUIRED_NEW, rollbackFor = Exception.class)
public void method(){

	readOnlyTxMethod(); // @Transactional(readOnly = true)
	writeTxMethod(); // @Transactional(rollbackFor = Exception.class)

}

 

 

method()에 지정된 @Transactional으로 인해 read-only Tx와 write Tx가 하나로 묶여 있어 

하나의 트랜잭션의 범위가 너무 넓어졌습니다.

read-only Tx와 write Tx는 서로 상호 배타적이면서 연동되는 데이터베이스도 다르기 때문에 

트랜잭션을 분리하는 것이 맞다고 생각되었습니다.

 

public void method(){

	readOnlyTxMethod(); // @Transactional(readOnly = true)
	writeTxMethod(); // @Transactional(rollbackFor = Exception.class)

}

 

여기서 고민해볼 것이 readOnly = true와 readOnly = false의 트랜잭션이 별개로 생성된다고는 들었지만

과연 맞을까? 라는 의문이 들었고 트랜잭션이 동일한지 판단이 필요했습니다. 

 

트랜잭션 매니저 클래스를 DataSourceTransactionManager를 사용하고 있습니다. 

이 클래스는 트랜잭션을 관리하는 클래스이며 계층 구조가 다음과 같습니다.

 

 

CciLocalTransactionManager, HibernateTransactionManager, JpaTransacationManager... 모두 추상 클래스인 AbstractPlatformTransactionManager를 상속받습니다.

 

 

트랜잭션이 시작되면 AbstractPlatformTransactionManager에서 트랜잭션을 획득하고자 getTransaction()이 호출됩니다.

이 메서드는 다양한 조건에 맞는 트랜잭션을 반환합니다.

아래는 getTransaction() 코드 전문입니다.

 

중단점을 이용하여 'if(this.isExistingTransaction(transaction))' 이 조건을 만족하여 기존 트랜잭션이 반환되는지 여부와 

else 구문이 실행되어 새로운 트랜잭션이 생성 여부가 관건이며 readOnlyTxMethod() , writeTxMethod()가 별개의 트랜잭션으로 수행되는 것을 확인했습니다.

 

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
   Object transaction = this.doGetTransaction();
   boolean debugEnabled = this.logger.isDebugEnabled();
   if (definition == null) {
       definition = new DefaultTransactionDefinition();
   }

   if (this.isExistingTransaction(transaction)) {
       return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
   } else if (((TransactionDefinition)definition).getTimeout() < -1) {
       throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout());
   } else if (((TransactionDefinition)definition).getPropagationBehavior() == 2) {
       throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
   } else if (((TransactionDefinition)definition).getPropagationBehavior() != 0 && ((TransactionDefinition)definition).getPropagationBehavior() != 3 && ((TransactionDefinition)definition).getPropagationBehavior() != 6) {
       if (((TransactionDefinition)definition).getIsolationLevel() != -1 && this.logger.isWarnEnabled()) {
           this.logger.warn("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: " + definition);
       }

       boolean newSynchronization = this.getTransactionSynchronization() == 0;
       return this.prepareTransactionStatus((TransactionDefinition)definition, (Object)null, true, newSynchronization, debugEnabled, (Object)null);
   } else {
       SuspendedResourcesHolder suspendedResources = this.suspend((Object)null);
       if (debugEnabled) {
           this.logger.debug("Creating new transaction with name [" + ((TransactionDefinition)definition).getName() + "]: " + definition);
       }

       try {
           boolean newSynchronization = this.getTransactionSynchronization() != 2;
           DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
           this.doBegin(transaction, (TransactionDefinition)definition);
           this.prepareSynchronization(status, (TransactionDefinition)definition);
           return status;
       } catch (RuntimeException var7) {
           this.resume((Object)null, suspendedResources);
           throw var7;
       } catch (Error var8) {
           this.resume((Object)null, suspendedResources);
           throw var8;
       }
   }
}

'[개발] 프레임워크 > Spring' 카테고리의 다른 글

Spring AOP  (1) 2024.06.26
Spring MVC  (0) 2024.06.23
Actuator  (0) 2023.12.26
Swagger  (0) 2023.12.26
[MyBatis] TypeHandler  (0) 2023.12.12