`
longgangbai
  • 浏览: 7251088 次
  • 性别: 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真正实现了分布式事务的管理。
附:org.springframework.transaction.jta.JotmFactoryBean在spring2.5.6中可以拷贝直接用
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics