转载
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_(); } }