Mybatis 와 같은 DB 연동 라이브러리를 사용해서 DB에 연동할 때 Spring Transaction 을 사용하는 경우와 개발자가 직접 Transaction 을 관리하는 2가지 경우가 있다. 이 경우 내부적으로 어떻게 동작하는지 차이점을 알면 대용량 트랜젝션 처리에 대비를 할 수 있다. 세부적인 동작을 알기 위해 다음 샘플 프로그램을 실행해 보자.
<Spring 및 Mybatis 내부 동작을 파악하기 위한 Log level 설정>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<!--?xml version="1.0" encoding="UTF-8"?--> <!-- Appenders --> < appender name = "console" class = "org.apache.log4j.ConsoleAppender" > < param name = "Target" value = "System.out" > < layout class = "org.apache.log4j.PatternLayout" > < param name = "ConversionPattern" value = "%-5p: %c - %m%n" > </ layout > </ appender > < logger name = "org.springframework.jdbc" > < level value = "debug" > </ level ></ logger > < logger name = "org.springframework.transaction" > < level value = "debug" > </ level ></ logger > < logger name = "java.sql" > < level value = "debug" > </ level ></ logger > < logger name = "java.sql.Connection" > < level value = "debug" > </ level ></ logger > < logger name = "java.sql.Statement" > < level value = "debug" > </ level ></ logger > < logger name = "org.mybatis" > < level value = "debug" > </ level ></ logger > <!-- Root Logger --> < root > < priority value = "info" > < appender-ref ref = "console" > </ appender-ref ></ priority ></ root > </ log4j:configuration > |
<Mybatis 기반의 DB 연동 프로그램 - Spring Transaction 을 사용하지 않는 경우>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
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 을 사용하는 경우>
1 2 3 4 5 6 7 8 9 10 |
... @Transactional public String invoke(){ System.out.println( "*** invoke start" ); insert1(); insert2(); System.out.println( "*** invoke end" ); return "transaction invoked" ; } ... |
여기서는 설명의 편의를 위해 Mybatis 나 Spring 설정을 모두 생략 하였다. 위의 2가지 프로그램을 실행했을 경우 출력 로그는 다음과 같다. (설명의 편의를 위해 일부 로그는 생략함)
<Spring transaction 을 사용하지 않았을 경우>
1 2 3 4 5 6 7 8 9 10 11 12 |
*** 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 을 사용했을 경우에는 로그 출력값이 바뀐다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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 방식인 경우)에 대한 다음과 같은 시사점을 얻을 수 있다.
- Transaction 은 method 단위로 관리 된다. => method 가 끝날 때까지 commit 또는 connection 반환이 이루어지지 않는다.
- Transaction 대상 method 내에서 발생하는 SQL 은 동일한 Connection 을 사용한다. (Propagation 정책에 따라 별도 Connection 사용 가능)
따라서 처리 시간이 긴 method 의 경우에는 Transaction 단위를 조정해서 DB Lock 지속시간이 지나치게 길어지거나 DB connection pool 이 모자라지 않도록 해야 한다.
[출처] Spring Transaction 내부 동작 메커니즘|작성자 예영아빠
'Mybatis > 노하우정보' 카테고리의 다른 글
mybatis-resultType을 배열로 (1) | 2016.04.27 |
---|---|
MyBatis에서 Batch처리(SqlSession과 foreach) (0) | 2016.04.27 |
MyBatis 내장 cache 에 대해서 (0) | 2016.04.27 |
Spring Transaction propagation mode에 따른 내부 동작 메커니즘 (0) | 2016.04.27 |
TransactionTemplate 을 이용한 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 |