Spring 中集成 JOTM 配置 JTA 事務:java
假如業務中要用到多個數據庫,咱們但願在業務方法中,當對某一個數據庫的數據表進行操做的事務失敗並回退(rollback),另外某一個數據庫的數據表的操做事務也要回退,但應用通常的事務管理達不到這樣的事務管理效果,這就須要實現 JTA 事務管理了。
這裏咱們在SPring中集成 Object web 的一個開源JTA實現JOTM (能夠在http://jotm.objectweb.org下載完整版) 來實現JTA事務管理。mysql
一、將必須的類包放入類路徑中:
jotm.jar, xapool.jar, jotm_jrmp_stubs.jar, jta-spect1_0_1.jar, connector-1_5.jar等等。web
二、編寫JOTM配置文件carol.properties,將其放到類路徑下:spring
- #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
三、在MYSQL中建立兩個數據庫 "jtatesta","jtatestb":sql
- 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);
四、域對象、數據訪問類和其餘事務管理的同樣,如:數據庫
-
- package com.domain;
- import java.io.Serializable;
- public class User implements Serializable {
- private int user_id;
- private String user_name;
- private String user_password;
- ......
- }
-
-
- package com.domain;
- import java.io.Serializable;
- public class Grade implements Serializable{
- private int grade_id;
- private User user;
- private double grade;
- .....
- }
-
- 應用Spring JDBC的DAO:(省略DAO接口)
-
- 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);
- }
- }
-
- 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);
}
}
五、應用了JTA事務管理的業務類(省略了接口),用@Transactional註解標註,以在配置文件中能夠用<tx:annotation-driven>註解驅動自動進行事務加強:服務器
- 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){
-
-
-
- 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);
}
}
六、spring爲JOTM提供了一個org.springframework.transaction.jta.JotmFactoryBean 支持類,能夠用其方便地建立本地JOTM實例。
具體的配置文件app_jta.xml以下:app
- <?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">
-
- <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
-
- <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
- <property name="userTransaction" ref="jotm"/>
- </bean>
- <!--XAPool配置,內部包含了一XA數據源,對應了數據庫jtatesta
- 支持JTA事務的數據源,必須封裝成XAPool-->
- <bean id="jtaTestADS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"
- destroy-method="shutdown">
- <property name="dataSource">
- <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>
-
- <bean id="jtaTestBDS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"
- destroy-method="shutdown">
- <property name="dataSource">
- <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>
-
-
- <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>
-
-
- <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>
-
-
- <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>
七、測試main方法:dom
- 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.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
.......測試