728x90
반응형

Mybatis 와 같은 DB 연동 라이브러리를 사용해서 DB에 연동할 때 Spring Transaction 을 사용하는 경우와 개발자가 직접 Transaction 을 관리하는 2가지 경우가 있다. 이 경우 내부적으로 어떻게 동작하는지 차이점을 알면 대용량 트랜젝션 처리에 대비를 할 수 있다. 세부적인 동작을 알기 위해 다음 샘플 프로그램을 실행해 보자.

 

<Spring 및 Mybatis 내부 동작을 파악하기 위한 Log level 설정>

 
  
      
      
          
          
              
          
      

      
          
      

      
          
      

      
          
      

      
          
          

      
          
          

      
          
              

      
      
          
          
      
  



 <Mybatis 기반의 DB 연동 프로그램 - Spring Transaction 을 사용하지 않는 경우>

package tkstone.test.transaction; 
 
public class TransactionInvoker {
    private Mapper1 mapper1; 
     
    public void setMapper1(Mapper1 mapper1){ 
        this.mapper1 = mapper1; 
    } 
     
    public String invoke(){ 
        System.out.println("*** invoke start"); 
        insert1(); 
        insert2(); 
        System.out.println("*** invoke end"); 
        return "transaction invoked"; 
    } 
 
    public void insert1(){ 
        A1 a1 = new A1(); 
        a1.col1 = "col1"; 
        a1.col2 = "col2"; 
        mapper1.insertA1(a1); 
    } 
     
    public void insert2(){ 
        A2 a2 = new A2(); 
        a2.col1 = "col1"; 
        a2.col2 = "col2"; 
        mapper1.insertA2(a2);         
    } 
} // End of TransactionInvoker 


 

 <Mybatis 기반의 DB 연동 프로그램 - Spring Transaction 을 사용하는 경우>

 ... 
     @Transactional 
     public String invoke(){ 
         System.out.println("*** invoke start"); 
         insert1(); 
         insert2(); 
         System.out.println("*** invoke end"); 
         return "transaction invoked"; 
     } 
 ...


여기서는 설명의 편의를 위해 Mybatis 나 Spring 설정을 모두 생략 하였다. 위의 2가지 프로그램을 실행했을 경우 출력 로그는 다음과 같다. (설명의 편의를 위해 일부 로그는 생략함)

 

<Spring transaction 을 사용하지 않았을 경우>

*** invoke start
DEBUG: org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
DEBUG: org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [jdbc:mysql://localhost:3306/test, UserName=test@localhost, MySQL Connector Java] will not be managed by Spring
DEBUG: org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession 
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
DEBUG: org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
DEBUG: org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [jdbc:mysql://localhost:3306/test, UserName=test@localhost, MySQL Connector Java] will not be managed by Spring
DEBUG: org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession 
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
*** invoke end



위의 로그를 보면 다음과 같은 특징이 있다.

- insert1() 이 호출 될 때 DB connection 을 얻어 와서 SQL을 실행한 후 DB connection 을 반환한다.

- insert2() 이 호출될 때 동일한 동작을 반복한다.

- insert1()과 insert2()는 Mybatis SqlSession 객체 및 DB Transaction을 공유하지 않는다.

- DB connection 은 auto commit mode 이므로 별도의 commit 이 발생하지 않는다.

 

반면 Spring transaction 을 사용했을 경우에는 로그 출력값이 바뀐다.

DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Creating new transaction 
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Acquired Connection for JDBC transaction
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Switching JDBC Connection to manual commit

*** invoke start
DEBUG: org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
DEBUG: org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession
DEBUG: org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection will be managed by Spring
DEBUG: org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession 
DEBUG: org.mybatis.spring.SqlSessionUtils - Fetched SqlSession from current transaction
DEBUG: org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession 
*** invoke end

DEBUG: org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession 
DEBUG: org.mybatis.spring.SqlSessionUtils - Transaction synchronization deregistering SqlSession 
DEBUG: org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession 
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction commit
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Committing JDBC transaction on Connection 
DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection after transaction
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource

위의 로그를 보면 Spring TransactionManager (DatasSourceTransactionManager)가 Transaction 관리를 하는 것을 알 수 있다. 

여기서 중요한 특징은 다음과 같다. 

- invoke() method 가 시작되기 전에 DB Connection 을 얻어 와서 Autocommit = false 설정을 한다.

- invoke() method 내에서는 하나의 Mybatis SqlSession 객체를 사용해서 insert1() 및 insert2() 를 실행한다. 즉 2개의 insert 가 실행될 때 동일한 DB Connection 을 사용한다.

- invoke() method 가 실행된 이후 Transaction 을 commit 하고 connection 을 반환한다.

 

여기서 Transaction 이 시작하는 지점은 invoke() 메소드에 대한 Proxy method 내부이다. 따라서 다음과 같이 호출 된다. 

호출 Bean -> Proxy.invoke() 시작 -> Transaction 시작 -> TransactionInvoker.invoke() 처리 완료 -> Transaction commit -> Proxy.invoke() 종료 

 

위의 로그를 보면 Spring Transaction(AOP 방식인 경우)에 대한 다음과 같은 시사점을 얻을 수 있다.

 

  1. Transaction 은 method 단위로 관리 된다. => method 가 끝날 때까지 commit 또는 connection 반환이 이루어지지 않는다.
  2. Transaction 대상 method 내에서 발생하는 SQL 은 동일한 Connection 을 사용한다. (Propagation 정책에 따라 별도 Connection 사용 가능)

 

따라서 처리 시간이 긴 method 의 경우에는 Transaction 단위를 조정해서 DB Lock 지속시간이 지나치게 길어지거나 DB connection pool 이 모자라지 않도록 해야 한다.

 

 

728x90
반응형
블로그 이미지

nineDeveloper

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

,