728x90
반응형

앞서 Spring AOP 를 이용한 Transaction 사용법을 설명 하였다. 특히 @Transactional 을 사용한 Transaction 선언이 편리하기는 하나 다음과 같은 경우에는 동작을 하지 않는다.

 

  1 public class TransactionInvoker2 {
  2  
  3  private A1Dao a1dao;
  4  private A2Dao a2dao;
  5 
  6  public void setA1dao(A1Dao dao){
  7   this.a1dao = dao;
  8  }
  9  
 10  public void setA2dao(A2Dao dao){
 11   this.a2dao = dao;
 12  }
 13  
 14  // 외부에서 호출하는 method
 15  public void invoke() throws Exception{
 16   doInternalTransaction();
 17  }
 18  
 19  @Transactional
 20  public void doInternalTransaction() throws Exception{
 21   a1dao.insertA1();
 22   a2dao.insertA2();
 23  }
 24 }

 

위의 프로그램에 Spring Transaction 이 적용되지 않는 이유는 앞의 포스팅에서 언급 했듯이 Proxy 방식으로 동작하기 때문이다. 여기서 invoke() 가 호출하는 대상 method 는 Proxy 의 doInternalTransaction() 이 아닌 실제 doInternalTransaction() 이다. Proxy 는 클래스 외부에서 호출하는 경우에만 동작 한다.

그러면 invoke() 에 @Transactional 을 설정하는 것을 고려할 수 있으나 다음 문제가 발생한다.

- Spring Transaction 은 method 단위로 동작 한다. 이것은 Method 시작 시점에 DB Connection 객체를 얻고 Method 종료 시점에 commit 후 DB Connection 을 반납한다는 것을 의미한다.

- 따라서 처리 시간이 긴 method 인 경우에는 불필요하게 오랫동안 DB Connection 을 점유하게 된다.

- 또한 DB Lock 이 유지되는 시간이 길어진다. (DBMS 종류에 따라 이 문제가 심각한 경우가 있다) 

 

따라서 위와 같은 유형의 프로그램에서는 개발자가 Transaction 의 시작 및 종료 시점을 결정할 필요가 있다. 이것을 위해서 Spring 에서는 TransactionTemplate을 이용하는 방법을 제공한다.

  1 import org.springframework.transaction.PlatformTransactionManager;
  2 import org.springframework.transaction.support.TransactionTemplate;
  3 import org.springframework.transaction.TransactionDefinition;
  4 import org.springframework.transaction.support.TransactionCallbackWithoutResult;
  5 import org.springframework.transaction.TransactionStatus;
  6 
  7 public class TransactionInvoker2 {
  8  
  9  private A1Dao a1dao;
 10  private A2Dao a2dao;
 11  private TransactionTemplate transactionTemplate;
 12 
 13  public void setA1dao(A1Dao dao){
 14   this.a1dao = dao;
 15  }
 16  
 17  public void setA2dao(A2Dao dao){
 18   this.a2dao = dao;
 19  }
 20  
 21  public void setTransactionManager(PlatformTransactionManager transactionManager){
 22   this.transactionTemplate = new TransactionTemplate(transactionManager);
 23   this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
 24  }
 25  
 26  public void invoke() throws Exception{
 27   doInternalTransaction();
 28  }
 29  
 30  private void doInternalTransaction() throws Exception{
 31   transactionTemplate.execute(new TransactionCallbackWithoutResult(){
 32    public void doInTransactionWithoutResult(TransactionStatus status){
 33     try{
 34      a1dao.insertA1();
 35      a2dao.insertA2();
 36     }
 37     catch(Exception e){
 38      status.setRollbackOnly();
 39     }
 40     return;
 41    }
 42   });
 43  }
 44 }

 

- 21 라인에서 Spring에서 TransactionManager 를 주입 받는다. 이 때 TransactionTemplate 을 생성 및 Transaction 속성을 설정한다. (실제로 Transaction 을 실행하는 부분에서 처리해도 무방하다.)

- 30 ~ 43 라인에서는 TransactionTemplate.execute() 내에서 로직을 실행한다.

 

위의 Bean 이 동작하기 위해서 Spring context 에 다음과 같이 선언해야 한다.

  1 <bean id="TransactionInvoker4" class="tkstone.test.transaction.TransactionInvoker4">
  2     <property name="a1dao" ref="a1dao"/>
  3     <property name="a2dao" ref="a2dao"/>
  4     <property name="transactionManager" ref="txManager1"/>
  5 </bean> 

여기서 TransactionManager 를 선언하는 방법은 앞의 글 (Spring Transaction) 을 참조하기 바란다.

 

시중에 나온 Spring 관련 서적에서는 Business Logic 과 Transaction 의 분리 필요성을 언급하고 있다. 대부분의 경우에는 올바른 접근 방법이다. 그러나 DB transaction 이 아주 중요한 서비스에서는 Business logic 구현 시 Transaction을 같이 고려해야 한다. 특히 DBMS 종류에 따른 Lock 지속 시간이나 Read consistency 차이 및 이로 인한 서비스 동시성 문제, 처리 성능이 문제 되는 경우에는 위와 같이 프로그램에 의한 Transaction 조정이 필요하다.

 

 

728x90
반응형
블로그 이미지

nineDeveloper

안녕하세요 현직 개발자 입니다 ~ 빠르게 변화하는 세상에 뒤쳐지지 않도록 우리모두 열심히 공부합시다 ~! 개발공부는 넘나 재미있는 것~!

,