앞서 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 조정이 필요하다.
'Mybatis > 노하우정보' 카테고리의 다른 글
MyBatis에서 Batch처리(SqlSession과 foreach) (0) | 2016.04.27 |
---|---|
MyBatis 내장 cache 에 대해서 (0) | 2016.04.27 |
Spring Transaction propagation mode에 따른 내부 동작 메커니즘 (0) | 2016.04.27 |
Spring Transaction 내부 동작 메커니즘 (0) | 2016.04.27 |
Spring Transaction 사용법 (0) | 2016.04.27 |
Spring - Mybatis 에서 Mapper interface 를 주입받는 방법과 SqlSession 을 주입받는 방법 (0) | 2016.04.27 |
Spring 에서 Mybatis 사용하기 (0) | 2016.04.27 |
Spring에서 mybatis 연동시 sqlSession 주입 시기 (0) | 2016.04.27 |