Spring 中集成 JOTM 配置 JTA 事务

 

Spring 中集成 JOTM 配置 JTA 事务:

假如业务中要用到多个数据库,我们希望在业务方法中,当对某一个数据库的数据表进行操作的事务失败并回退(rollback),另外某一个数据库的数据表的操作事务也要回退,但应用一般的事务管理达不到这样的事务管理效果,这就需要实现 JTA 事务管理了。
这里我们在SPring中集成 Object web 的一个开源JTA实现JOTM (可以在http://jotm.objectweb.org 下载完整版) 来实现JTA事务管理。

1、将必须的类包放入类路径中:
 jotm.jar, xapool.jar, jotm_jrmp_stubs.jar, jta-spect1_0_1.jar, connector-1_5.jar等等。

2、编写JOTM配置文件carol.properties,将其放到类路径下:

Java代码
  1. #JNDI调用协议   
  2. carol.protocols=jrmp   
  3. #不使用CAROL JNDI封装器   
  4. carol.start.jndi= false   
  5. #不启动命名服务器   
  6. carol.start.ns= false   

 
3、在MYSQL中创建两个数据库 "jtatesta","jtatestb":

Java代码
  1. CREATE DATABASE IF NOT EXISTS jtatesta;   
  2. USE jtatesta;   
  3.   
  4. DROP TABLE IF EXISTS `user`;   
  5. CREATE TABLE `user` (   
  6.   `user_id`  int ( 10 ) unsigned NOT NULL auto_increment,   
  7.   `user_name` varchar( 45 ) NOT NULL,   
  8.   `user_password` varchar( 45 ) NOT NULL,   
  9.   PRIMARY KEY  (`user_id`)   
  10. ) ENGINE=InnoDB AUTO_INCREMENT= 5  DEFAULT CHARSET=latin1;   
  11.   
  12. INSERT INTO `user` (`user_id`,`user_name`,`user_password`) VALUES    
  13.  ( 1 , 'tufu' , 'tufu' );   
  14.   
  15. CREATE DATABASE IF NOT EXISTS jtatestb;   
  16. USE jtatestb;   
  17.   
  18. DROP TABLE IF EXISTS `grade`;   
  19. CREATE TABLE `grade` (   
  20.   `grade_id`  int ( 10 ) unsigned NOT NULL auto_increment,   
  21.   `user_id`  int ( 10 ) unsigned NOT NULL,   
  22.   `grade`  double  NOT NULL,   
  23.   PRIMARY KEY  (`grade_id`)   
  24. ) ENGINE=InnoDB AUTO_INCREMENT= 9  DEFAULT CHARSET=latin1;   
  25.   
  26. INSERT INTO `grade` (`grade_id`,`user_id`,`grade`) VALUES    
  27.  ( 1 , 0 , 100 );  

 
4、域对象、数据访问类和其他事务管理的一样,如:

Java代码
  1. //Domain对象User.java:   
  2. package  com.domain;   
  3. import  java.io.Serializable;   
  4. public   class  User  implements  Serializable {   
  5.      private   int  user_id;   
  6.      private  String user_name;   
  7.      private  String user_password;   
  8. ...... //省略set、get方法   
  9. }   
  10.   
  11. //Domain对象Grade.java:   
  12. package  com.domain;   
  13. import  java.io.Serializable;   
  14. public   class  Grade  implements  Serializable{   
  15.      private   int  grade_id;   
  16.      private  User user;   
  17.      private   double  grade;   
  18. ..... //省略set、get方法   
  19. }   
  20.   
  21. 应用Spring JDBC的DAO:(省略DAO接口)   
  22. //UserJdbcDao.java:   
  23. package  com.dao.jdbc;   
  24. import  org.springframework.jdbc.core.support.JdbcDaoSupport;   
  25. import  com.dao.UserDao;   
  26. import  com.domain.User;   
  27. public   class  UserJdbcDao  extends  JdbcDaoSupport  implements  UserDao{   
  28.      public   void  addUser(User user){   
  29.         String SQL =  "INSERT INTO user(user_id,user_name,user_password) VALUES(?,?,?)" ;   
  30.         Object[] params =  new  Object[]{   
  31.             user.getUser_id(),user.getUser_name(),user.getUser_password()   
  32.         };   
  33.          this .getJdbcTemplate().update(SQL, params);   
  34.     }   
  35. }   
  36. //GradeJdbcDao.java:   
  37. package  com.dao.jdbc;   
  38. import  com.dao.GradeDao;   
  39. import  com.domain.Grade;   
  40. import  org.springframework.jdbc.core.support.JdbcDaoSupport;   
  41. public   class  GradeJdbcDao  extends  JdbcDaoSupport  implements  GradeDao{   
  42.      public   void  addGrade(Grade grade){   
  43.          final  String SQL =  "INSERT INTO grade(user_id,grade) VALUES(?,?)" ;   
  44.         Object[] params =  new  Object[]{   
  45.             grade.getUser().getUser_id(),grade.getGrade()   
  46.         };   
  47.          this .getJdbcTemplate().update(SQL, params);   
  48.     }   
  49. }  

 
5、应用了JTA事务管理的业务类(省略了接口),用@Transactional注解标注,以在配置文件中可以用<tx:annotation-driven>注解驱动自动进行事务增强:

Java代码
  1. package  com.service.impl;   
  2. import  com.dao.GradeDao;   
  3. import  com.dao.UserDao;   
  4. import  com.domain.*;   
  5. import  org.springframework.transaction.annotation.Transactional;   
  6. import  com.service.MyService;   
  7. @Transactional   
  8. public   class  MyServiceImpl  implements  MyService {   
  9.      private  UserDao userDao;   
  10.      private  GradeDao gradeDao;   
  11.      public   void  setUserDao(UserDao userDao){   
  12.          this .userDao = userDao;   
  13.     }   
  14.      public   void  setGradeDao(GradeDao gradeDao){   
  15.          this .gradeDao = gradeDao;   
  16.     }   
  17.      @Transactional (readOnly= false )   
  18.      public   void  addGrade(User user,Grade grade){   
  19.          //假如希望两个添加数据的事务,其中有一个添加失败时,均回滚,   
  20.          //由于两个操作是在两个不同的数据库上进行的,故要JTA事务来进行管理   
  21.          //否则,将会出现添加一个,回滚一个的情形   
  22.         gradeDao.addGrade(grade);    
  23.         userDao.addUser(user);   
  24.     }   
  25. }  

 
6、spring为JOTM提供了一个org.springframework.transaction.jta.JotmFactoryBean 支持类,可以用其方便地创建本地JOTM实例。
具体的配置文件app_jta.xml如下:

Xml代码
  1. <? xml   version = "1.0"   encoding = "UTF-8" ?>   
  2. < beans   xmlns = "http://www.springframework.org/schema/beans"   
  3.      xmlns:xsp = "http://www.w3.org/2001/XMLSchema-instance"   
  4.      xmlns:aop = "http://www.springframework.org/schema/aop"   
  5.      xmlns:tx = "http://www.springframework.org/schema/tx"   
  6.      xsp:schemaLocation ="http://www.springframework.org/schema/beans   
  7.     http://www.springframework.org/schema/beans/spring-beans-2.0.xsd   
  8.     http://www.springframework.org/schema/aop   
  9.     http://www.springframework.org/schema/aop/spring-aop-2.0.xsd   
  10.     http://www.springframework.org/schema/tx   
  11.     http://www.springframework.org/schema/tx/spring-tx-2.0.xsd" >   
  12.     <!--JOTM本地实例-->   
  13.      < bean   id = "jotm"   class = "org.springframework.transaction.jta.JotmFactoryBean" />   
  14.      <!--JTA事务管理器-->   
  15.      < bean   id = "txManager"   class = "org.springframework.transaction.jta.JtaTransactionManager" >   
  16.          < property   name = "userTransaction"   ref = "jotm" /> <!--指定userTransaction属性引用JOTM本地实例-->   
  17.      </ bean >   
  18.     <!--XAPool配置,内部包含了一XA数据源,对应了数据库jtatesta   
  19.     支持JTA事务的数据源,必须封装成XAPool-- >   
  20.      < bean   id = "jtaTestADS"   class = "org.enhydra.jdbc.pool.StandardXAPoolDataSource"   
  21.      destroy-method = "shutdown" >   
  22.          < property   name = "dataSource" > <!--内部XA数据源-->   
  23.              < bean   class = "org.enhydra.jdbc.standard.StandardXADataSource"   
  24.              destroy-method = "shutdown" >   
  25.                  < property   name = "transactionManager"   ref = "jotm" />   
  26.                  < property   name = "driverName"   value = "com.mysql.jdbc.Driver" />   
  27.                  < property   name = "url"   value = "jdbc:mysql://localhost/jtatesta" />   
  28.              </ bean >   
  29.          </ property >   
  30.          < property   name = "user"   value = "root" />   
  31.          < property   name = "password"   value = "885123" />   
  32.      </ bean >   
  33.      <!--类似地,对应了数据库jtatestb的XAPool配置,内部包含了一XA数据源-->   
  34.      < bean   id = "jtaTestBDS"   class = "org.enhydra.jdbc.pool.StandardXAPoolDataSource"   
  35.      destroy-method = "shutdown" >   
  36.          < property   name = "dataSource" > <!--内部XA数据源-->   
  37.              < bean   class = "org.enhydra.jdbc.standard.StandardXADataSource"   
  38.              destroy-method = "shutdown" >   
  39.                  < property   name = "transactionManager"   ref = "jotm" />   
  40.                  < property   name = "driverName"   value = "com.mysql.jdbc.Driver" />   
  41.                  < property   name = "url"   value = "jdbc:mysql://localhost/jtatestb" />   
  42.              </ bean >   
  43.          </ property >   
  44.          < property   name = "user"   value = "root" />   
  45.          < property   name = "password"   value = "885123" />   
  46.      </ bean >   
  47.   
  48.      <!--分别配置访问jtaTestADS、jtaTestBDS数据源的Spring JDBC模板-->   
  49.      < bean   id = "jtaTestATemplate"   class = "org.springframework.jdbc.core.JdbcTemplate" >   
  50.          < property   name = "dataSource"   ref = "jtaTestADS" />   
  51.      </ bean >   
  52.      < bean   id = "jtaTestBTemplate"   class = "org.springframework.jdbc.core.JdbcTemplate" >   
  53.          < property   name = "dataSource"   ref = "jtaTestBDS" />   
  54.      </ bean >   
  55.   
  56.      <!--分别配置基于模板jtaTestADS,jtaTestBDS的DAO-->   
  57.      < bean   id = "userDao"   class = "com.dao.jdbc.UserJdbcDao" >   
  58.          < property   name = "jdbcTemplate"   ref = "jtaTestATemplate" />   
  59.      </ bean >   
  60.      < bean   id = "gradeDao"   class = "com.dao.jdbc.GradeJdbcDao" >   
  61.          < property   name = "jdbcTemplate"   ref = "jtaTestBTemplate" />   
  62.      </ bean >   
  63.   
  64.      <!--跨数据库的JTA事务的业务类-->   
  65.      < bean   id = "myService"   class = "com.service.impl.MyServiceImpl" >   
  66.          < property   name = "userDao"   ref = "userDao" />   
  67.          < property   name = "gradeDao"   ref = "gradeDao" />   
  68.      </ bean >   
  69.   <!--注解事务驱动-->   
  70.      < tx:annotation-driven   transaction-manager = "txManager"   proxy-target-class = "true" />   
  71.   
  72. </ beans >   

 

7、测试main方法:

Java代码
  1. import  com.service.MyService;   
  2. import  com.service.impl.MyServiceImpl;   
  3. import  com.domain.*;   
  4. import  org.springframework.context.support.ClassPathXmlApplicationContext;   
  5.   
  6. public   class  TestMain {   
  7.      public   static   void  main(String args[]){   
  8.         ClassPathXmlApplicationContext ctx =   
  9.                  new  ClassPathXmlApplicationContext( "beans_jta.xml" );   
  10.         MyService ms = (MyServiceImpl)ctx.getBean( "myService" );   
  11.         User user =  new  User();   
  12. //特意添加一个重复的主键,以使添加user的事务失败并回退   
  13. //如果此时应用JTA事务失败,将仍会执行添加grade的事务并提交(前提是先于添加user操作)   
  14. //如果应用JTA事务成功,就会两个添加事务同时执行或同时回退。   
  15.         user.setUser_id( 1 );    
  16.         user.setUser_name( "tufu" );   
  17.         user.setUser_password( "tufu" );   
  18.         Grade grade =  new  Grade();   
  19.         grade.setGrade( 100 );   
  20.         grade.setUser(user);   
  21.   
  22.         ms.addGrade(user,grade);   
  23.     }   
  24. }   
  25.   
  26.    

 注:将log4j.properties中的log4j日志设置为DEBUG级别,可以看到详细的JTA事务执行情况:
.......
log4j.rootLogger=DEBUG,R,A1
.......

 

 在特定应用服务器使用JTA
    一般来说,Spring的事务抽象与应用服务器是无关的。不过,如果你如果希望事务管理器使用特定的UserTransaction 和 TransactionManager 对象(可以被自动探测),以获取更多的增强事务语义。这时,针对不同的Java EE应用服务器,Spring的事务管理器可以采取不同的配置。 3 d0 W4 y5 k3 u, h! G

 BEA WebLogic

    在一个使用WebLogic 7.0、8.1或更高版本的环境中,你一般会优先选用特定于WebLogic的 WebLogicJtaTransactionManager 类来取代基础的 JtaTransactionManager 类,因为在WebLogic环境中,该类提供了对Spring事务定义的完全支持,超过了标准的JTA语义。你可以使用以下的配置达到目的:
<bean id="txManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager"/> 7 n6 e" n' j: m9 X- Q
它的特性包括:支持事务名,支持为每个事务定义隔离级别,以及在任何环境下正确地恢复事务的能力。

 IBM WebSphere
. |' v# o% K7 }( K& B
    在WebSphere 5.1、5.0和4.x环境下,你可以使用Spring的 WebSphereTransactionManagerFactoryBean 类。这是一个工厂类,通过WebSphere的静态访问方法(每个版本的WebSphere中都不同)获取到JTA TransactionManager 实例。一旦通过工厂bean获取到JTA TransactionManager 实例,就可以使用该实例装配一个Spring的 JtaTransactionManager bean,它封装了JTA UserTransaction,提供增强的事务语义。你可以按以下方式进行配置: 8 |6 Y+ C: C: k6 C

①引用WebSphere的JTA事务管理器

< bean id = " wsJtaTm " class = " org.springframework.transaction.jta.WebSphereTransactionManagerFactoryBean " />

< bean id = " transactionManager " class = " org.springframework.transaction.jta.JtaTransactionManager " >

< property name = " transactionManager ref= " wsJtaTm " />

</ bean >

    小结
    你既可以在没有任务应用服务器支持的情况下,直接通过集成JOTM在Spring中使用JTA事务管理,也可以通过引用Java EE应用服务器的JNDI数据源,利用应用服务器提供的JTA事务功能间接实现Spring 中的JTA事务管理。为了利用一些高级Java EE应用服务器的JTA事务高级功能,你可以通过Spring所提供的特定于应用服务器的JTA事务管理器进行配置。

 

你可能感兴趣的:(spring)