JTA를 이용한 글로벌/분산 트랜잭션
링크 : http://springsource.tistory.com/138
한 개 이상의 DB나 JMS의 작업을 하나의 트랜잭션 안에서 동작하게 하려면 서버가 제공하는 트랜잭션 매니저를 JTA를 통해 사용해야 한다. 스프링에서는 서버에 설정해둔 XA DataSource와 트랜잭션 매니저 그리고 UserTransaction 등을 JNDI를 통해 가져와 모든 데이터 액세스 기술에서 사용할 수 있다. JTA와 분산/글로벌 트랜잭션을 사용하기 위한 설정은 자바 서버마다 다르므로 해당 서버의 매뉴얼을 참고해서 등록하는 방법을 알아둬야 한다.
JTA가 적용된 다음 설정을 살펴보자
1 2 3 4 5 6 7 8 9 10 11 | <jee:jndi-lookup id= "dataSource1" jndi-name= "jdbc/xaDS1" /> <bean id= "memberDao" class = "...MemberDao" > <property name= "dataSource" ref= "dataSource1" /> </bean> <jee:jndi-lookup id= "dataSource2" jndi-name= "jdbc/xaDS2" /> <bean id= "usageDao" class = "...UsageDao" > <property name= "dataSource" ref= "dataSource2" /> </bean> <bean id= "txManager" class = "org.springframework.transaction.jta.JtaTransactionManager" /> |
이 설정에는 두 개의 DataSource 타입 빈이 등장한다. 이 두 개의 DataSource 빈은 스프링 안에서 만들어지는게 아니라 서버에서 제공해주는 DataSource를 JNDI로 가져온 것이다. 서버에는 분산 트랜잭션을 위한 XA 프로토콜을 지원하는 XA DataSource 두 개가 jdbc/xaDS1과 jdbc/xaDS2 라는 JNDI 이름으로 등록되어 있어야 한다. 이 두개의 DataSource를 사용하는 DAO도 각각 등록해준다. DAO의 데이터 액세스 기술은 어떤 것이든 상관없다.
마지막으로 JtaTransactionManager를 빈으로 등록한다. JtaTransactionManager는 여타 트랜잭션 매니저와는 다르게 프로퍼티로 DataSource나 SessionFactory 등의 빈을 참조하지 않는다. 대신 서버에 등록된 트랜잭션 매니저를 가져와 JTA를 이용해서 트랜잭션을 관리해줄 뿐이다. 이미 서버의 JTA 서비스에는 JNDI로 가져온 두 개의 XA DataSource가 등록되어 있을 것이다. JTA는 서버에서 미리 설정해두기만 하면 스프링에서 사용하는 것은 어떤 트랜잭션 매니저보다 간단하다.
JtaTransactionManager는 JNDI를 통해 JTA TransactionManager와 JTA UserTransaction을 찾아온다. 이때 사용하는 JNDI 이름은 WAS에서 자주 사용되는 기본적인 이름을 이용한다.
UserTransaction은 "java:comp/UserTransaction"을 사용하고, TransactionManager는 "java:comp/TransactionManager", "java:appserver/TransactionManager", "java:pm/TransactionManager", "java:/TransactionManager" 네 가지 이름을 시도해서 가장 먼저 발견되는 것을 사용한다.
기본 이름이 아닌 JNDI 이름으로 UserTransaction이나 TransactionManager가 등록되어 있다면 JtaTransactionManager의 transactionManagerName 과 userTransactionName 프로퍼티를 이용해서 JNDI 이름을 지정해줘야 한다.
독립형 JTA 트랜잭션 매니저
JTA는 WAS가 제공하는 서비스를 이용하는 경우가 일반적이지만, 원한다면 서버의 지원 없이도 애플리케이션 안에 JTA 서비스 기능을 내장하는 독립형 JTA 방식으로 이용할 수 있다. 이 방식을 사용하면 JTA를 지원하는 WAS가 아닌 톰캣과 같은 서블릿 컨테이너에서도 JTA 기능을 이용하는 것이 가능하다. 서버에 포함돼서 서비스로 동작하는 것은 아니지만 JTA의 자중 트랜잭션 리소스를 위한 글로벌 트랜잭션 기능을 활용할 수 있다. 스프링 안에서 간단히 설정을 추가하는 것만으로 JTA의 기능을 사용할 수 있다는 점에서 매력적이다.
독립형 JTA 트랜잭션 매니저는 ObjectWeb의 JTA 엔진인 JOTM(http://jotm.objectweb.org) 와 Atomikos의 TransactionalEssentials(http://www.atomikos.com) 가 대표적이다. 두 가지 모두 오픈소스 제품이므로 자유롭게 가져다 쓸 수 있다. Atomikos에서는 오픈소스외에도 고급 기능을 가진 상용 제품인 ExtremeTransactions 를 판매하기도 한다.
이 두 가지 모두 스프링의 JtaTransactionManager와 결합해서 JTA 트랜잭션 서비스로 사용할 수 있다.
Atomikos의 TransactionEssentials 를 스프링에 적용한 예를 살펴보자.
먼저 다음과 같이 Atomikos의 JTA TransactionManager와 JTA UserTransaction을 빈으로 등록한다. 이 두 가지 JTA 서비스는 서버에서 제공해주지 않기 때문에 스프링의 빈으로 등록해서 서비스를 이용해야 한다. 스프링의 트랜잭션 매니저와 여기서 사용한 JTA 트랜잭션 매니저는 다른 것이므로 혼동해서는 안 된다. 스프링의 JtaTransactionManager 는 JTA 트랜잭션 매니저를 스프링 애플리케이션이 이용하게 해주는 트랜잭션 추상화를 위한 클래스일 뿐이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <bean id= "atomikosTransactionManager" class = "com.atomikos.icatch.jta.UserTransactionManager" init-method= "init" destroy-method= "close" > <property name= "forceShutdown" > <value> true </value> </property> </bean> <bean id= "atomikosUserTransaction" class = "com.atomikos.icatch.jta.UserTransactionImp" > <property name= "transactionTimeout" > <value> 300 </value> </property> </bean> |
다음은 아래와 같이 스프링 JtaTransactionManager를 등록하고 서버의 트랜잭션 서비스 대신 앞에서 빈으로 등록해둔 Atomikos JTA 서비스를 이용하도록 프로퍼티를 설정해줘야 한다.
1 2 3 4 5 | <bean id= "transactionManager" class = "org.springframework.transaction.jta.JtaTransactionManager" > <property name= "transactionManager" ref= "atomikosTransactionManager" /> <property name= "userTransaction" ref= "atomikosUserTransaction" /> </bean> |
다음은 XA를 지원하는 DataSource를 빈으로 등록할 차례다. 글로벌/분산 트랜잭션을 사용하는 만큼 여러 개의 DB를 사용하도록 하나 이상의 DataSource를 등록한다. DataSource는 JTA 트랜잭션 매니저와 XA 프로토콜을 통한 트랜잭션이 동작하도록 만들어야 한다. 따라서 일반 DataSource 대신 XA를 지원하는 XA DataSource를 사용해야 한다. Atomikos 는 XA 지원 드라이브를 사용할 수도 있고, XA를 지원하지 않는 드라이버를 Atomikos의 도움을 통해 XA 드라이버처럼 사용하게 만들 수도 있다. 여기서는 MySQL이 제공하는 XA DataSource인 MysqlXADataSource를 사용해보겠다.
먼저 다음과 같이 첫 번째 DB를 위한 DataSource를 등록해보자. JTA에서 사용할 DataSource에는 고유한 리소스 이름을 지정해 줘야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <bean id= "dataSource1" class = "com.atomikos.jdbc.AtomikosDataSourceBean" init-method= "init" destroy-method= "close" > <property name= "uniqueResourceName" value= "MySQLXA1" /> <property name= "xaDataSourceClassName" value= "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name= "xaProperties" > <props> <prop key= "user" >jtauser</prop> <prop key= "password" >jtapassword</prop> <prop key= "url" >jdbc:mysql: //localhost/tx1</prop> </props> </property> <property name= "poolSize" value= "1" /> </bean> |
같은 방법으로 다음과 같이 두 번째와 세 번째 DataSource를 등록한다.
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 | <bean id= "dataSource2" class = "com.atomikos.jdbc.AtomikosDataSourceBean" init-method= "init" destroy-method= "close" > <property name= "uniqueResourceName" value= "MySQLXA2" /> <property name= "xaDataSourceClassName" value= "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name= "xaProperties" > <props> <prop key= "user" >jtauser</prop> <prop key= "password" >jtapassword</prop> <prop key= "url" >jdbc:mysql: //localhost/tx1</prop> </props> </property> <property name= "poolSize" value= "1" /> </bean> <bean id= "dataSource3" class = "com.atomikos.jdbc.AtomikosDataSourceBean" init-method= "init" destroy-method= "close" > <property name= "uniqueResourceName" value= "MySQLXA3" /> <property name= "xaDataSourceClassName" value= "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name= "xaProperties" > <props> <prop key= "user" >jtauser</prop> <prop key= "password" >jtapassword</prop> <prop key= "url" >jdbc:mysql: //localhost/tx1</prop> </props> </property> <property name= "poolSize" value= "1" /> </bean> |
이제 각각의 데이터 소스를 사용하는 JDBC DAO나 iBatis SqlMapClient 또는 JPA EntityManagerFactory, 하이버네이트 SessionFactory 를 등록하고 DAO를 만든다. 이때 각 데이터 액세스 기술에서 사용되는 DataSourceTransactionManager 나 JpaTransactionManager 같은 트랜잭션 매니저는 등록할 필요가 없다.
이제 모든 준비가 끝났다. 세 가지 DB를 사용하는, 다른 기술로 만들어진 DAO들이 JTA를 통해 하나의 트랜잭션 안에서 동작함을 확인할 수 있을 것이다.
WAS 트랜잭션 매니저의 고급 기능 사용하기
스프링의 JtaTransactionManager 는 JTA의 표준 스펙을 따르는 API를 사용해 트랜잭션을 관리한다. 그런데 WebLogic 이나 OC4J, WebSphere 등의 고급 WAS에서는 표준 JTA는 지원하지 않는, WAS가 제공하는 고급 트랜잭션 기능을 활용할 수 있다.
WAS별 전용 트랜잭션 매니저를 살펴보자
WebSphereUowTransactionManager
WebSphereUowTransactionManager 를 JtaTransactionManager 대신 사용하면 IBM WebSphere의 UOWManager 를 통해서 WebSphere 가 제공하는 트랜잭션 서비스의 기능을 최대한 활용할 수 있다. IBM WebSphere팀의 공식적인 기술지원을 통해서 개발된 만큼 안정성을 보장받을 수 있다. JTA에서는 기본적으로 보장되지 않는 트랜잭션 일시중단 기능이 제공되며, 이를 통해 REQUIRES_NEW 같은 트랜잭션 전파 속성을 사용할 수 있다.
WebLogicJtaTransactionManager
WebLogic 서버의 트랜잭션 서비스를 최대한 활용할 수 있게 해준다. 트랜잭션 이름, 트랜잭션별 격리수준 설정, 트랜잭션의 일시중지와 재시작 등을 모두 활용할 수 있다. 또, WebLogic 서버의 트랜잭션 모니터를 통해 스프링에서 진행되는 트랜잭션을 관찰할 수 있게 해준다.
OC4JJtaTransactionManager
OC4J 서버의 트랜잭션 기능에 최적화된 트랜잭션 매니저다. 트랜잭션별 격리수준 설정을 지원하면 OC4J의 트랜잭션 모니터에서 스프링의 트랜잭션을 볼 수 있게 해준다. 오라클팀이 만들어서 스프링에 제공한 코드를 바탕으로 만들어졌다.
위 세 가지 서버를 사용하는 경우라면 JtaTransactionManager 대신 해당 서버 전용 트랜잭션 매니저를 사용하는 편이 좋다. 서버에 따른 트랜잭션 매니저의 종류를 변경하기가 귀찮다면, JtaTransactionManager 빈을 등록하는 대신 스프링이 제공하는 서버 자동인식 기능을 가진 전용 태그를 사용할 수 있다.
1 | <tx:jta-transaction-manager/> |
tx 스키마의 jta-transaction-manager 태그를 이용해 JTA 트랜잭션 매니저를 등록할 수 있다. 이 방식의 장점은 서버를 자동인식해서 적절한 JTA 트랜잭션매니저를 등록해준다는 점이다. 이 설정을 가진 애플리케이션을 WebSphere에 가져가면 WebSphere 용 JTA 트랜잭션 매니저가 등록되고, OC4J에 배치하면 OC4J용 트랜잭션 매니저가 자동등록된다. 세 개의 서버 외에 배치됐을 때는 기본인 JtaTransactionManager가 사용된다.