程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Spring集成JOTM的JTA事務管理

Spring集成JOTM的JTA事務管理

編輯:關於JAVA

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;
......//省略set、get方法 
}
//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;
.....//省略set、get方法 
} 

應用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;
......//省略set、get方法
}
//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;
.....//省略set、get方法
}

應用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>
   <!--類似地,對應了數據庫jtatestb的XAPool配置,內部包含了一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>
   <!--分別配置訪問jtaTestADS、jtaTestBDS數據源的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,jtaTestBDS的DAO-->
   <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>
   <!--類似地,對應了數據庫jtatestb的XAPool配置,內部包含了一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>
   <!--分別配置訪問jtaTestADS、jtaTestBDS數據源的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,jtaTestBDS的DAO-->
   <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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved