[转]Spring+iBatis+JOTM实现JTA事务

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中可以拷贝直接用

你可能感兴趣的:(spring,ibatis,jotm,JTA事务)