`
baggioback
  • 浏览: 71266 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Spring+iBatis+JOTM实现JTA事务

 
阅读更多
JOTM是个开源的JTA事务管理组件,可以让程序脱离J2EE容器而获得分布式事务管理的能力。
 
测试过程如下:
 
一、环境
 
1、准备软件环境

 spring-framework-2.5.6.SEC01-with-dependencies.zip
 ibatis-2.3.4
 ow2-jotm-dist-2.1.4-bin.tar.gz
 MySQL-5.1
 JDK1.5
 
2、创建数据库环境,注意数据库引擎为InnoDB,只有这样才能支持事务。
 
CREATE DATABASE IF NOT EXISTS testdb_a    DEFAULT CHARACTER SET utf8;

USE testdb_a;

DROP TABLE IF EXISTS tab_a;

CREATE TABLE tab_a (
    id bigint (20) NOT NULL ,
    name varchar (60) DEFAULT NULL ,
    address varchar (120) DEFAULT NULL ,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE DATABASE IF NOT EXISTS testdb_b    DEFAULT CHARACTER SET utf8;

USE testdb_b;

DROP TABLE IF EXISTS tab_b;

CREATE TABLE tab_b (
    id bigint (20) NOT NULL ,
    name varchar (60) DEFAULT NULL ,
    address varchar (120) DEFAULT NULL ,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


二、建立项目testJOTM
 
1、建立项目后,准备依赖的类库,结构如下:
│    spring-aop.jar
│    spring-beans.jar
│    spring-context-support.jar
│    spring-context.jar
│    spring-core.jar
│    spring-jdbc.jar
│    spring-jms.jar
│    spring-orm.jar
│    spring-test.jar
│    spring-tx.jar
│    spring-web.jar
│    spring-webmvc-portlet.jar
│    spring-webmvc-struts.jar
│    spring-webmvc.jar
│    aspectjrt.jar
│    aspectjweaver.jar
│    cglib-nodep-2.1_3.jar
│    asm-2.2.3.jar
│    log4j-1.2.15.jar
│    asm-commons-2.2.3.jar
│    asm-util-2.2.3.jar
│    aopalliance.jar
│    mysql-connector-java-5.1.6-bin.jar

├─ibatis
│            ibatis-2.3.4.726.jar
│            sql-map-2.dtd
│            sql-map-config-2.dtd

├─jotm
│            license.txt
│            xapool.jar
│            jotm-core.jar
│            jotm-standalone.jar
│            jotm-jms.jar
│            jotm-datasource.jar
│            ow2-jta-1.1-spec.jar
│            jotm-client.jar

├─jakarta-commons
│            commons-attributes-api.jar
│            commons-attributes-compiler.jar
│            commons-beanutils.jar
│            commons-codec.jar
│            commons-collections.jar
│            commons-dbcp.jar
│            commons-digester.jar
│            commons-discovery.jar
│            commons-fileupload.jar
│            commons-httpclient.jar
│            commons-io.jar
│            commons-lang.jar
│            commons-logging.jar
│            commons-pool.jar
│            commons-validator.jar

├─junit
│            junit-3.8.2.jar
│            junit-4.4.jar
│            license.txt

└─log4j
                log4j-1.2.15.jar
 
2、根据表建立entity和SQLMap
public class TabA implements Serializable {
        private Long id;
        private String name;
        private String address;
        //省略getter/setter
 
public class TabB implements Serializable {
        private Long id;
        private String name;
        private String address;
        //省略getter/setter

TabA.xml
<? xml version ="1.0" encoding ="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd" >

<!-- 表名:tab_a -->
< sqlMap namespace ="tab_a" >
        < typeAlias alias ="TabA" type ="com.lavasoft.stu.jtom.entity.TabA" />
        < resultMap id ="result_base" class ="TabA" >
                < result property ="id" column ="id" />
                < result property ="name" column ="name" />
                < result property ="address" column ="address" />
        </ resultMap >
        <!-- 添加 -->
        < insert id ="insert" parameterClass ="TabA" >
                insert into tab_a(
                id,
                name,
                address
                ) values (
                #id#,
                #name#,
                #address#
                )
                < selectKey keyProperty ="id" resultClass ="long" >
                        select LAST_INSERT_ID()
                </ selectKey >
        </ insert >
        <!-- 更新 -->
        < update id ="update" parameterClass ="TabA" >
                update tab_a set
                id = #id#,
                name = #name#,
                address = #address#
                where id = #id#
        </ update >
        <!-- 删除 -->
        < delete id ="deleteById" parameterClass ="long" >
                delete from tab_a
                where id = #value#
        </ delete >
        <!-- 根据ID获取 -->
        < select id ="findById" parameterClass ="long" resultMap ="tab_a.result_base" >
                select *
                from tab_a
                where id = #value#
        </ select >
</ sqlMap >
 
TabB.xml
<? xml version ="1.0" encoding ="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd" >

<!-- 表名:tab_b -->
< sqlMap namespace ="tab_b" >
        < typeAlias alias ="TabB" type ="com.lavasoft.stu.jtom.entity.TabB" />
        < resultMap id ="result_base" class ="TabB" >
                < result property ="id" column ="id" />
                < result property ="name" column ="name" />
                < result property ="address" column ="address" />
        </ resultMap >
        <!-- 添加 -->
        < insert id ="insert" parameterClass ="TabB" >
                insert into tab_b(
                id,
                name,
                address
                ) values (
                #id#,
                #name#,
                #address#
                )
                < selectKey keyProperty ="id" resultClass ="long" >
                        select LAST_INSERT_ID()
                </ selectKey >
        </ insert >
        <!-- 更新 -->
        < update id ="update" parameterClass ="TabB" >
                update tab_b set
                id = #id#,
                name = #name#,
                address = #address#
                where id = #id#
        </ update >
        <!-- 删除 -->
        < delete id ="deleteById" parameterClass ="long" >
                delete from tab_b
                where id = #value#
        </ delete >
        <!-- 根据ID获取 -->
        < select id ="findById" parameterClass ="long" resultMap ="tab_b.result_base" >
                select *
                from tab_b
                where id = #value#
        </ select >
</ sqlMap >
 
/**
* TabADAO
*
* @author leizhimin 2009-6-25 12:39:19
*/

public interface TabADAO {

        /**
         * 保存一个TabA对象
         *
         * @param tabA TabA对象
         * @return 返回保存后的对象
         */

        TabA saveTabA(TabA tabA);

        /**
         * 更新一个TabA
         *
         * @param tabA TabA对象
         * @return 返回更新后的对象
         */

        TabA updateTabA(TabA tabA);

        /**
         * 删除指定标识的一个TabA
         *
         * @param id TabA标识
         */

        void deleteTabAById(Long id);

        /**
         * 获取指定标识的TabA
         *
         * @param id TabA标识
         * @return 所查询到的TabA
         */

        TabA findTabAById(Long id);
}
 
/**
* TabADAO
*
* @author leizhimin 2009-6-25 12:43:55
*/

public class TabADAOImpl extends SqlMapClientDaoSupport implements TabADAO {

        /**
         * 保存一个TabA对象
         *
         * @param tabA TabA对象
         * @return 返回保存后的对象
         */

        public TabA saveTabA(TabA tabA) {
                Long id = (Long) getSqlMapClientTemplate().insert("tab_a.insert" , tabA);
                tabA.setId(id);
                return tabA;
        }

        /**
         * 更新一个TabA
         *
         * @param tabA TabA对象
         * @return 返回更新后的对象
         */

        public TabA updateTabA(TabA tabA) {
                getSqlMapClientTemplate().update("tab_a.update" , tabA);
                return tabA;
        }

        /**
         * 删除指定标识的一个TabA
         *
         * @param id TabA标识
         */

        public void deleteTabAById(Long id) {
                getSqlMapClientTemplate().delete("tab_a.deleteById" ,id);
        }

        /**
         * 获取指定标识的TabA
         *
         * @param id TabA标识
         * @return 所查询到的TabA
         */

        public TabA findTabAById(Long id) {
                return (TabA) getSqlMapClientTemplate().queryForObject("tab_a.findById" ,id);
        }
}
 
B的DAO和A类似,就不写了。
 
/**
* 测试JOTM的Service
*
* @author leizhimin 2009-6-25 12:53:55
*/

public interface StuJotmService {
        /**
         * 同时保存TabA、TabB
         *
         * @param a TabA对象
         * @param b TabB对象
         */

        void saveAB(TabA a, TabB b);

        /**
         * 同时更新TabA、TabB
         *
         * @param a TabA对象
         * @param b TabB对象
         */

        void updateAB(TabA a, TabB b);

        /**
         * 删除指定id的TabA、TabB记录
         *
         * @param id 指定id
         */

        void deleteABif(Long id);
}
 
/**
* Created by IntelliJ IDEA.
*
* @author leizhimin 2009-6-25 12:58:48
*/

//@Transactional
public class StuJotmServiceImpl implements StuJotmService {
        private TabADAO tabADAO;
        private TabBDAO tabBDAO;

        /**
         * 同时保存TabA、TabB
         *
         * @param a TabA对象
         * @param b TabB对象
         */

//        @Transactional(readOnly=false)
        public void saveAB(TabA a, TabB b) {
                tabADAO.saveTabA(a);
                tabBDAO.saveTabB(b);
        }

        /**
         * 同时更新TabA、TabB
         *
         * @param a TabA对象
         * @param b TabB对象
         */

//        @Transactional(readOnly=false)
        public void updateAB(TabA a, TabB b) {
                tabADAO.updateTabA(a);
                tabBDAO.updateTabB(b);
        }

        /**
         * 删除指定id的TabA、TabB记录
         *
         * @param id 指定id
         */

//        @Transactional(readOnly=false)
        public void deleteABif(Long id) {
                tabADAO.deleteTabAById(id);
                tabBDAO.deleteTabBById(id);
        }

        public void setTabADAO(TabADAO tabADAO) {
                this .tabADAO = tabADAO;
        }

        public void setTabBDAO(TabBDAO tabBDAO) {
                this .tabBDAO = tabBDAO;
        }
}
 
/**
* Spring上下文工具
*
* @author leizhimin 2008-8-13 14:42:58
*/


public class ApplicationContextUtil {
        private static ApplicationContext applicationContext;

        static {
                if (applicationContext == null )
                        applicationContext = rebuildApplicationContext();
        }

        /**
         * 重新构建Spring应用上下文环境
         *
         * @return ApplicationContext
         */

        public static ApplicationContext rebuildApplicationContext() {
                return new ClassPathXmlApplicationContext("/ApplicationContext.xml" );
        }

        /**
         * 获取Spring应用上下文环境
         *
         * @return
         */

        public static ApplicationContext getApplicationContext() {
                return applicationContext;
        }

        /**
         * 简单的上下文环境测试
         */

        public static void main(String[] args) {
                rebuildApplicationContext();
                if (applicationContext == null ) {
                        System.out.println("ApplicationContext is null " );
                } else {
                        System.out.println("ApplicationContext is not null !" );
                }
        }
}
 
三、做JTOM、Spring、iBatis、Log4j等配置
 
JOTM配置:carol.properties
#JNDI调用协议
carol.protocols=jrmp
#不使用CAROL JNDI封装器        
carol.start.jndi=false
#不启动命名服务器
carol.start.ns=false
 
Spring配置:ApplicationContext.xml
<? xml version ="1.0" encoding ="UTF-8" ?>
<!-- 局部单元测试使用,不正式发布,不要删除 -->
< beans xmlns ="http://www.springframework.org/schema/beans"
             xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:jee ="http://www.springframework.org/schema/jee"
             xmlns:aop ="http://www.springframework.org/schema/aop"
             xmlns:tx ="http://www.springframework.org/schema/tx"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                     http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-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">

        <!--指定Spring配置中用到的属性文件 -->
        < bean id ="propertyConfig"
                    class ="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
                < property name ="locations" >
                        < list >
                                < value > classpath:jdbc.properties</ value >
                        </ list >
                </ property >
        </ bean >
        <!-- JOTM实例 -->
        < bean id ="jotm" class ="org.springframework.transaction.jta.JotmFactoryBean" />
        <!-- JTA事务管理器 -->
        < bean id ="myJtaManager"
                    class ="org.springframework.transaction.jta.JtaTransactionManager" >
                < property name ="userTransaction" >
                        < ref local ="jotm" />
                </ property >
        </ bean >
        <!-- 数据源A -->
        < bean id ="dataSourceA" 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 ="${jdbc.driver}" />
                                < property name ="url" value ="${jdbc.url}" />
                        </ bean >
                </ property >
                < property name ="user" value ="${jdbc.username}" />
                < property name ="password" value ="${jdbc.password}" />
        </ bean >
        <!-- 数据源B -->
        < bean id ="dataSourceB" 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 ="${jdbc2.driver}" />
                                < property name ="url" value ="${jdbc2.url}" />
                        </ bean >
                </ property >
                < property name ="user" value ="${jdbc2.username}" />
                < property name ="password" value ="${jdbc.password}" />
        </ bean >
        <!-- 事务切面配置 -->
        < aop:config >
                < aop:pointcut id ="serviceOperation"
                                            expression ="execution(* *..servi1ce*..*(..))" />
                < aop:advisor pointcut-ref ="serviceOperation"
                                         advice-ref ="txAdvice" />
        </ aop:config >
        <!-- 通知配置 -->
        < tx:advice id ="txAdvice" transaction-manager ="myJtaManager" >
                < tx:attributes >
                        < tx:method name ="delete*" rollback-for ="Exception" />
                        < tx:method name ="save*" rollback-for ="Exception" />
                        < tx:method name ="update*" rollback-for ="Exception" />
                        < tx:method name ="*" read-only ="true" rollback-for ="Exception" />
                </ tx:attributes >
        </ tx:advice >

        <!--根据dataSourceA和sql-map-config_A.xml创建一个SqlMapClientA -->
        < bean id ="sqlMapClientA"
                    class ="org.springframework.orm.ibatis.SqlMapClientFactoryBean" >
                < property name ="dataSource" >
                        < ref local ="dataSourceA" />
                </ property >
                < property name ="configLocation" >
                        < value > sql-map-config_A.xml</ value >
                </ property >
        </ bean >
        <!--根据dataSourceB和sql-map-config_B.xml创建一个SqlMapClientB -->
        < bean id ="sqlMapClientB"
                    class ="org.springframework.orm.ibatis.SqlMapClientFactoryBean" >
                < property name ="dataSource" >
                        < ref local ="dataSourceB" />
                </ property >
                < property name ="configLocation" >
                        < value > sql-map-config_B.xml</ value >
                </ property >
        </ bean >
        <!--根据sqlMapClientA创建一个SqlMapClientTemplate的模版类实例sqlMapClientTemplateA -->
        < bean id ="sqlMapClientTemplateA"
                    class ="org.springframework.orm.ibatis.SqlMapClientTemplate" >
                < property name ="sqlMapClient" ref ="sqlMapClientA" />
        </ bean >
        <!--根据sqlMapClientB创建一个SqlMapClientTemplate的模版类实例sqlMapClientTemplateB -->
        < bean id ="sqlMapClientTemplateB"
                    class ="org.springframework.orm.ibatis.SqlMapClientTemplate" >
                < property name ="sqlMapClient" ref ="sqlMapClientB" />
        </ bean >

        <!-- 配置DAO,并注入所使用的sqlMapClientTemplate实例 -->
        < bean id ="tabADAO" class ="com.lavasoft.stu.jtom.dao.impl.TabADAOImpl" >
                < property name ="sqlMapClientTemplate" ref ="sqlMapClientTemplateA" />
        </ bean >
        < bean id ="tabBDAO" class ="com.lavasoft.stu.jtom.dao.impl.TabBDAOImpl" >
                < property name ="sqlMapClientTemplate" ref ="sqlMapClientTemplateB" />
        </ bean >

        <!-- Service配置,注入DAO -->
        < bean id ="stuJotmService" class ="com.lavasoft.stu.jtom.service.StuJotmServiceImpl" >
                < property name ="tabADAO" ref ="tabADAO" />
                < property name ="tabBDAO" ref ="tabBDAO" />
        </ bean >
</ beans >
 
数据库配置:jdbc.properties
jdbc.database=cms_release
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.0.2:3306/testdb_a?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull
jdbc.username=root
jdbc.password=leizhimin

jdbc2.database=cms_release
jdbc2.driver=com.mysql.jdbc.Driver
jdbc2.url=jdbc:mysql://192.168.0.1:3306/testdb_b?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull
jdbc2.username=root
jdbc2.password=leizhimin
 
iBatis的SQLMap配置:
sql-map-config_A.xml
<? xml version ="1.0" encoding ="UTF-8" ?>
<!DOCTYPE sqlMapConfig
                PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
                "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

< sqlMapConfig >
        < settings cacheModelsEnabled ="true" enhancementEnabled ="true"
                            lazyLoadingEnabled ="true" errorTracingEnabled ="true"
                            useStatementNamespaces ="true" />

        < sqlMap resource ="com/lavasoft/stu/jtom/entity/sqlmap/TabA.xml" />

</ sqlMapConfig >
 
sql-map-config_B.xml
<? xml version ="1.0" encoding ="UTF-8" ?>
<!DOCTYPE sqlMapConfig
                PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
                "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

< sqlMapConfig >
        < settings cacheModelsEnabled ="true" enhancementEnabled ="true"
                            lazyLoadingEnabled ="true" errorTracingEnabled ="true"
                            useStatementNamespaces ="true" />

        < sqlMap resource ="com/lavasoft/stu/jtom/entity/sqlmap/TabB.xml" />

</ sqlMapConfig >
 
日志的配置:log4j.properties
log4j.rootLogger=INFO,CONSOLE,LOGFILE

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=INFO
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss } - %-5p %c        %x - %m%n

log4j.appender.LOGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.LOGFILE.File=contestlog.log
log4j.appender.LOGFILE.MaxFileSize=500KB
log4j.appender.LOGFILE.MaxBackupIndex=10
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss } -    %-p %c     %x - %m%n

log4j.logger.com.ibatis=INFO
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=INFO
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=INFO
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=INFO
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=INFO,CONSOLE
log4j.logger.java.sql.PreparedStatement=INFO,CONSOLE
 
四、测试
 
public class Test {
        private static ApplicationContext ctx = ApplicationContextUtil.getApplicationContext();
        private static StuJotmService ser = (StuJotmService) ctx.getBean("stuJotmService" );

        public static void test_() {
                TabA a = new TabA();
                a.setId(1L);
                a.setName("aaa" );
                a.setAddress("address a" );
                TabB b = new TabB();
                b.setId(1L);
                b.setName("bbb" );
                b.setAddress("address b" );
                ser.saveAB(a, b);
        }

        public static void main(String[] args) {
                test_();
        }
}
 
然后将主键其中一个改掉进行测试,让另外一个主键重复,发现a、b数据库均不能成功写入,经过多次测试,验证jtom真正实现了分布式事务的管理。
分享到:
评论

相关推荐

    spring+jotm+ibatis+mysql实现JTA分布式事务

    本项目“spring+jotm+ibatis+mysql实现JTA分布式事务”旨在利用这些技术来确保在分布式环境中的数据一致性。下面将详细介绍这个项目所涉及的知识点。 首先,Spring框架是Java开发中最常用的应用框架之一,它提供了...

    JTA事务源码示例

    Spring+iBatis+JOTM实现JTA事务: 如何处理跨库事物:spring + jtom 的jta事务是个很好的选择. 这个源码示例非常不错,包括所有的源码和jar包,下载后eclipse 或 myeclipse 导入就能用。 里面有详细的说明和注释,...

    跨多个数据库操作,同时连接两个一上的数据库,用事物关联起来

    Spring+iBatis+Atomikos实现JTA事务的例子,展示了如何结合使用Spring、iBatis和开源的Atomikos事务管理器来支持全局事务。Spring与Hibernate整合时,也可以配置多数据库事务,确保在操作多个数据库时的事务一致性。...

    跨多个数据库操作,同时连接多个的数据库,同时操作

    3. **JTA事务协调器**:如Atomikos、JOTM等,它们是实现JTA的开源事务管理器。这些协调器负责监控和管理跨越多个数据库的事务,确保在异常情况下进行回滚或提交。 4. **Spring与ORM框架整合**:Spring可以与...

    c3p0-0.9.1.2等等

    spring-ibatis spring-jdbc spring-jdo spring-jpa-2.0-m2 spring-struts spring-web spring-webmvc sqljdbc struts velocity-1.5 velocity-tools-1.3 xapool xerces-2.6.2 xml-apis (我做毕业设计时,用到的。需要...

    学生选课系统中所用的60个jar包

    oro.jar jakarta-oro-2.0.8.jar JAMon.jar jasperreports-1.0.2.jar jaxen-1.1-beta-4.jar jdbc2_0-stdext.jar jmxremote.jar jmxremote_optional.jar jmxri.jar jotm.jar jta.jar junit.jar jxl.jar mysql-...

Global site tag (gtag.js) - Google Analytics