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 飛熊 閱讀(651) 評論(0)  編輯  收藏 所屬分類: JTA


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導航

          統計

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          收藏夾

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 如东县| 广宁县| 奉节县| 合川市| 文安县| 永善县| 沈阳市| 柳州市| 红河县| 浦江县| 崇仁县| 长岛县| 桑植县| 贵港市| 贵阳市| 马山县| 永州市| 宣城市| 长岭县| 光泽县| 图们市| 平泉县| 芒康县| 佛山市| 清徐县| 拉萨市| 财经| 西昌市| 吉水县| 武安市| 米易县| 科技| 江山市| 云南省| 隆化县| 中阳县| 长葛市| 商城县| 柳州市| 冷水江市| 宜兰市|