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 代码

#JNDI调用协议 
carol.protocols=jrmp
 
#
不使用CAROL JNDI封装器 
carol.start.jndi=false
 
#
不启动命名服务器 
carol.start.ns=false
 
#JNDI
调用协议
carol.protocols=jrmp
#
不使用CAROL JNDI封装器
carol.start.jndi=false
#
不启动命名服务器
carol.start.ns=false

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

   Java 代码

CREATE DATABASE IF NOT EXISTS jtatesta; 
USE jtatesta;
 
DROP TABLE IF EXISTS `user`;
 
CREATE TABLE `user` (
 
 `user_id` int(10) unsigned NOT NULL auto_increment, 
 `user_name` varchar(45) NOT NULL, 
 `user_password` varchar(45) NOT NULL, 
 PRIMARY KEY (`user_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
 
INSERT INTO `user` (`user_id`,`user_name`,`user_password`) VALUES
  
(1,'tufu','tufu');
 
CREATE DATABASE IF NOT EXISTS jtatestb;
 
USE jtatestb;
 
DROP TABLE IF EXISTS `grade`;
 
CREATE TABLE `grade` (
 
 `grade_id` int(10) unsigned NOT NULL auto_increment, 
 `user_id` int(10) unsigned NOT NULL, 
 `grade` double NOT NULL, 
 PRIMARY KEY (`grade_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
 
INSERT INTO `grade` (`grade_id`,`user_id`,`grade`) VALUES
  
(1,0,100);
 
CREATE DATABASE IF NOT EXISTS jtatesta;
USE jtatesta;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
 `user_id` int(10) unsigned NOT NULL auto_increment,
 `user_name` varchar(45) NOT NULL,
 `user_password` varchar(45) NOT NULL,
 PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
INSERT INTO `user` (`user_id`,`user_name`,`user_password`) VALUES
(1,'tufu','tufu');
CREATE DATABASE IF NOT EXISTS jtatestb;
USE jtatestb;
DROP TABLE IF EXISTS `grade`;
CREATE TABLE `grade` (
 `grade_id` int(10) unsigned NOT NULL auto_increment,
 `user_id` int(10) unsigned NOT NULL,
 `grade` double NOT NULL,
 PRIMARY KEY (`grade_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
INSERT INTO `grade` (`grade_id`,`user_id`,`grade`) VALUES
(1,0,100);

 

 

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

   Java 代码

//Domain对象User.java: 
package com.domain;
 
import java.io.Serializable;
 
public class User implements Serializable {
 
  private int user_id; 
  private String user_name; 
  private String user_password; 
......//
省略setget方法 
}
 
//Domain
对象Grade.java: 
package com.domain;
 
import java.io.Serializable;
 
public class Grade implements Serializable{
 
  private int grade_id; 
  private User user; 
  private double grade; 
.....//
省略setget方法 
}
 

  应用 Spring JDBC DAO ( 省略 DAO 接口 )  

//UserJdbcDao.java: 
package com.dao.jdbc;
 
import org.springframework.jdbc.core.support.JdbcDaoSupport;
 
import com.dao.UserDao;
 
import com.domain.User;
 
public class UserJdbcDao extends JdbcDaoSupport implements UserDao{
 
  public void addUser(User user){ 
    String SQL = "INSERT INTO user(user_id,user_name,user_password) VALUES(?,?,?)"; 
    Object[] params = new Object[]{ 
      user.getUser_id(),user.getUser_name(),user.getUser_password() 
    }; 
    this.getJdbcTemplate().update(SQL, params); 
  } 
}
 
//GradeJdbcDao.java:
 
package com.dao.jdbc;
 
import com.dao.GradeDao;
 
import com.domain.Grade;
 
import org.springframework.jdbc.core.support.JdbcDaoSupport;
 
public class GradeJdbcDao extends JdbcDaoSupport implements GradeDao{
 
  public void addGrade(Grade grade){ 
    final String SQL = "INSERT INTO grade(user_id,grade) VALUES(?,?)"; 
    Object[] params = new Object[]{ 
      grade.getUser().getUser_id(),grade.getGrade() 
    }; 
    this.getJdbcTemplate().update(SQL, params); 
  } 
}
 
//Domain
对象User.java:
package com.domain;
import java.io.Serializable;
public class User implements Serializable {
  private int user_id;
  private String user_name;
  private String user_password;
......//
省略setget方法
}
//Domain
对象Grade.java:
package com.domain;
import java.io.Serializable;
public class Grade implements Serializable{
  private int grade_id;
  private User user;
  private double grade;
.....//
省略setget方法
}

 

 

应用 Spring JDBC DAO ( 省略 DAO 接口 )

//UserJdbcDao.java:
package com.dao.jdbc;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.dao.UserDao;
import com.domain.User;
public class UserJdbcDao extends JdbcDaoSupport implements UserDao{
  public void addUser(User user){
    String SQL = "INSERT INTO user(user_id,user_name,user_password) VALUES(?,?,?)";
    Object[] params = new Object[]{
      user.getUser_id(),user.getUser_name(),user.getUser_password()
    };
    this.getJdbcTemplate().update(SQL, params);
  }
}
//GradeJdbcDao.java:
package com.dao.jdbc;
import com.dao.GradeDao;
import com.domain.Grade;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class GradeJdbcDao extends JdbcDaoSupport implements GradeDao{
  public void addGrade(Grade grade){
    final String SQL = "INSERT INTO grade(user_id,grade) VALUES(?,?)";
    Object[] params = new Object[]{
      grade.getUser().getUser_id(),grade.getGrade()
    };
    this.getJdbcTemplate().update(SQL, params);
  }
}

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

   Java 代码

package com.service.impl; 
import com.dao.GradeDao;
 
import com.dao.UserDao;
 
import com.domain.*;
 
import org.springframework.transaction.annotation.Transactional;
 
import com.service.MyService;
 
@Transactional
 
public class MyServiceImpl implements MyService {
 
  private UserDao userDao; 
  private GradeDao gradeDao; 
  public void setUserDao(UserDao userDao){ 
    this.userDao = userDao; 
  } 
  public void setGradeDao(GradeDao gradeDao){ 
    this.gradeDao = gradeDao; 
  } 
  @Transactional(readOnly=false) 
  public void addGrade(User user,Grade grade){ 
    //假如希望两个添加数据的事务,其中有一个添加失败时,均回滚, 
    //由于两个操作是在两个不同的数据库上进行的,故要JTA事务来进行管理 
    //否则,将会出现添加一个,回滚一个的情形 
    gradeDao.addGrade(grade);  
    userDao.addUser(user); 
  } 
}
 
package com.service.impl;
import com.dao.GradeDao;
import com.dao.UserDao;
import com.domain.*;
import org.springframework.transaction.annotation.Transactional;
import com.service.MyService;
@Transactional
public class MyServiceImpl implements MyService {
  private UserDao userDao;
  private GradeDao gradeDao;
  public void setUserDao(UserDao userDao){
    this.userDao = userDao;
  }
  public void setGradeDao(GradeDao gradeDao){
    this.gradeDao = gradeDao;
  }
  @Transactional(readOnly=false)
  public void addGrade(User user,Grade grade){
    //假如希望两个添加数据的事务,其中有一个添加失败时,均回滚,
    //由于两个操作是在两个不同的数据库上进行的,故要JTA事务来进行管理
    //否则,将会出现添加一个,回滚一个的情形
    gradeDao.addGrade(grade);
    userDao.addUser(user);
  }
}

6 spring JOTM 提供了一个 org.springframework.transaction.jta.JotmFactoryBean 支持类,可以用其方便地创建本地 JOTM 实例。

  具体的配置文件 app_jta.xml 如下:

   Xml 代码

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
 
  xmlns:xsp="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:aop="http://www.springframework.org/schema/aop" 
  xmlns:tx="http://www.springframework.org/schema/tx" 
  xsp:schemaLocation="http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
  http://www.springframework.org/schema/aop 
  http://www.springframework.org/schema/aop/spring-aop-2.0.xsd 
  http://www.springframework.org/schema/tx 
  http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"> 
  <!--JOTM本地实例--> 
  <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/> 
  <!--JTA事务管理器--> 
  <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
    <property name="userTransaction" ref="jotm"/><!--指定userTransaction属性引用JOTM本地实例--> 
  </bean> 
  <!--XAPool配置,内部包含了一XA数据源,对应了数据库jtatesta 
  支持JTA事务的数据源,必须封装成XAPool--> 
  <bean id="jtaTestADS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" 
  destroy-method="shutdown"> 
    <property name="dataSource"><!--内部XA数据源--> 
      <bean class="org.enhydra.jdbc.standard.StandardXADataSource" 
      destroy-method="shutdown"> 
        <property name="transactionManager" ref="jotm"/> 
        <property name="driverName" value="com.mysql.jdbc.Driver"/> 
        <property name="url" value="jdbc:mysql://localhost/jtatesta"/> 
      </bean> 
    </property> 
    <property name="user" value="root"/> 
    <property name="password" value="885123"/> 
  </bean> 
  <!--类似地,对应了数据库jtatestbXAPool配置,内部包含了一XA数据源--> 
  <bean id="jtaTestBDS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" 
  destroy-method="shutdown"> 
    <property name="dataSource"><!--内部XA数据源--> 
      <bean class="org.enhydra.jdbc.standard.StandardXADataSource" 
      destroy-method="shutdown"> 
        <property name="transactionManager" ref="jotm"/> 
        <property name="driverName" value="com.mysql.jdbc.Driver"/> 
        <property name="url" value="jdbc:mysql://localhost/jtatestb"/> 
      </bean> 
    </property> 
    <property name="user" value="root"/> 
    <property name="password" value="885123"/> 
  </bean> 
  <!--分别配置访问jtaTestADSjtaTestBDS数据源的Spring JDBC模板--> 
  <bean id="jtaTestATemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
    <property name="dataSource" ref="jtaTestADS"/> 
  </bean> 
  <bean id="jtaTestBTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
    <property name="dataSource" ref="jtaTestBDS"/> 
  </bean> 
  <!--分别配置基于模板jtaTestADS,jtaTestBDSDAO--> 
  <bean id="userDao" class="com.dao.jdbc.UserJdbcDao"> 
    <property name="jdbcTemplate" ref="jtaTestATemplate"/> 
  </bean> 
  <bean id="gradeDao" class="com.dao.jdbc.GradeJdbcDao"> 
    <property name="jdbcTemplate" ref="jtaTestBTemplate"/> 
  </bean> 
  <!--跨数据库的JTA事务的业务类--> 
  <bean id="myService" class="com.service.impl.MyServiceImpl"> 
    <property name="userDao" ref="userDao"/> 
    <property name="gradeDao" ref="gradeDao"/> 
  </bean> 
<!--
注解事务驱动--> 
  <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/> 
</beans>
 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsp="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsp:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
  <!--JOTM本地实例-->
  <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
  <!--JTA事务管理器-->
  <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="userTransaction" ref="jotm"/><!--指定userTransaction属性引用JOTM本地实例-->
  </bean>
  <!--XAPool配置,内部包含了一XA数据源,对应了数据库jtatesta
  支持JTA事务的数据源,必须封装成XAPool-->
  <bean id="jtaTestADS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"
  destroy-method="shutdown">
    <property name="dataSource"><!--内部XA数据源-->
      <bean class="org.enhydra.jdbc.standard.StandardXADataSource"
      destroy-method="shutdown">
        <property name="transactionManager" ref="jotm"/>
        <property name="driverName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/jtatesta"/>
      </bean>
    </property>
    <property name="user" value="root"/>
    <property name="password" value="885123"/>
  </bean>
  <!--类似地,对应了数据库jtatestbXAPool配置,内部包含了一XA数据源-->
  <bean id="jtaTestBDS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"
  destroy-method="shutdown">
    <property name="dataSource"><!--内部XA数据源-->
      <bean class="org.enhydra.jdbc.standard.StandardXADataSource"
      destroy-method="shutdown">
        <property name="transactionManager" ref="jotm"/>
        <property name="driverName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/jtatestb"/>
      </bean>
    </property>
    <property name="user" value="root"/>
    <property name="password" value="885123"/>
  </bean>
  <!--分别配置访问jtaTestADSjtaTestBDS数据源的Spring JDBC模板-->
  <bean id="jtaTestATemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="jtaTestADS"/>
  </bean>
  <bean id="jtaTestBTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="jtaTestBDS"/>
  </bean>
  <!--分别配置基于模板jtaTestADS,jtaTestBDSDAO-->
  <bean id="userDao" class="com.dao.jdbc.UserJdbcDao">
    <property name="jdbcTemplate" ref="jtaTestATemplate"/>
  </bean>
  <bean id="gradeDao" class="com.dao.jdbc.GradeJdbcDao">
    <property name="jdbcTemplate" ref="jtaTestBTemplate"/>
  </bean>
  <!--跨数据库的JTA事务的业务类-->
  <bean id="myService" class="com.service.impl.MyServiceImpl">
    <property name="userDao" ref="userDao"/>
    <property name="gradeDao" ref="gradeDao"/>
  </bean>
<!--
注解事务驱动-->
  <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
</beans>

7 、测试 main 方法:

   Java 代码

import com.service.MyService; 
import com.service.impl.MyServiceImpl;
 
import com.domain.*;
 
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class TestMain {
 
  public static void main(String args[]){ 
    ClassPathXmlApplicationContext ctx = 
        new ClassPathXmlApplicationContext("beans_jta.xml"); 
    MyService ms = (MyServiceImpl)ctx.getBean("myService"); 
    User user = new User(); 
//
特意添加一个重复的主键,以使添加user的事务失败并回退 
//
如果此时应用JTA事务失败,将仍会执行添加grade的事务并提交(前提是先于添加user操作) 
//
如果应用JTA事务成功,就会两个添加事务同时执行或同时回退。 
    user.setUser_id(1);  
    user.setUser_name("tufu"); 
    user.setUser_password("tufu"); 
    Grade grade = new Grade(); 
    grade.setGrade(100); 
    grade.setUser(user); 
    ms.addGrade(user,grade); 
  } 
}
 
  
import com.service.MyService;
import com.service.impl.MyServiceImpl;
import com.domain.*;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMain {
  public static void main(String args[]){
    ClassPathXmlApplicationContext ctx =
        new ClassPathXmlApplicationContext("beans_jta.xml");
    MyService ms = (MyServiceImpl)ctx.getBean("myService");
    User user = new User();
//
特意添加一个重复的主键,以使添加user的事务失败并回退
//
如果此时应用JTA事务失败,将仍会执行添加grade的事务并提交(前提是先于添加user操作)
//
如果应用JTA事务成功,就会两个添加事务同时执行或同时回退。
    user.setUser_id(1);
    user.setUser_name("tufu");
    user.setUser_password("tufu");
    Grade grade = new Grade();
    grade.setGrade(100);
    grade.setUser(user);
    ms.addGrade(user,grade);
  }
}

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

   .......

   log4j.rootLogger=DEBUG,R,A1

http://mrzhangtufu.javaeye.com/blog/241594

posted on 2009-12-09 16:41 飞熊 阅读(632) 评论(0)  编辑  收藏 所属分类: JTA


只有注册用户登录后才能发表评论。


网站导航:
 
<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿(1)

随笔分类

随笔档案

文章分类

文章档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜