1 环境
1.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
ibatis-2.3.4
ow2-jotm-dist-2.1.4-bin.tar.gz
MySQL-5.1
JDK1.5
1.2 创建数据库环境
注意mysql里数据库引擎为InnoDB,只有这样才能支持事务。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 CREATE DATABASE IF NOT EXISTS testdb_a DEFAULT CHARACTER SET utf8; 2 3 USE testdb_a; 4 5 DROP TABLE IF EXISTS tab_a; 6 7 CREATE TABLE tab_a ( 8 id bigint(20) NOT NULL, 9 name varchar(60) DEFAULT NULL, 10 address varchar(120) DEFAULT NULL, 11 PRIMARY KEY (id) 12 ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 13 14 15 CREATE DATABASE IF NOT EXISTS testdb_b DEFAULT CHARACTER SET utf8; 16 17 USE testdb_b; 18 19 DROP TABLE IF EXISTS tab_b; 20 21 CREATE TABLE tab_b ( 22 id bigint(20) NOT NULL, 23 name varchar(60) DEFAULT NULL, 24 address varchar(120) DEFAULT NULL, 25 PRIMARY KEY (id) 26 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2 建立项目testJOTM
2.1 建立项目后,准备依赖的类库,结构如下
2.2 主要代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * 测试JOTM的Service 3 * 4 * @author leizhimin 2009-6-25 12:53:55 5 */ 6 public interface StuJotmService { 7 /** 8 * 同时保存TabA、TabB 9 * 10 * @param a 11 * TabA对象 12 * @param b 13 * TabB对象 14 */ 15 void saveAB(TabA a, TabB b); 16 17 /** 18 * 同时更新TabA、TabB 19 * 20 * @param a 21 * TabA对象 22 * @param b 23 * TabB对象 24 */ 25 void updateAB(TabA a, TabB b); 26 27 /** 28 * 删除指定id的TabA、TabB记录 29 * 30 * @param id 31 * 指定id 32 */ 33 void deleteABif(Long id); 34 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class StuJotmServiceImpl implements StuJotmService { 2 private TabADAO tabADAO; 3 private TabBDAO tabBDAO; 4 5 /** 6 * 同时保存TabA、TabB 7 * 8 * @param a 9 * TabA对象 10 * @param b 11 * TabB对象 12 */ 13 // @Transactional(readOnly=false) 14 public void saveAB(TabA a, TabB b) { 15 tabADAO.saveTabA(a); 16 tabBDAO.saveTabB(b); 17 } 18 19 /** 20 * 同时更新TabA、TabB 21 * 22 * @param a 23 * TabA对象 24 * @param b 25 * TabB对象 26 */ 27 // @Transactional(readOnly=false) 28 public void updateAB(TabA a, TabB b) { 29 tabADAO.updateTabA(a); 30 tabBDAO.updateTabB(b); 31 } 32 33 /** 34 * 删除指定id的TabA、TabB记录 35 * 36 * @param id 37 * 指定id 38 */ 39 // @Transactional(readOnly=false) 40 public void deleteABif(Long id) { 41 tabADAO.deleteTabAById(id); 42 tabBDAO.deleteTabBById(id); 43 } 44 45 public void setTabADAO(TabADAO tabADAO) { 46 this.tabADAO = tabADAO; 47 } 48 49 public void setTabBDAO(TabBDAO tabBDAO) { 50 this.tabBDAO = tabBDAO; 51 } 52 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 局部单元测试使用,不正式发布,不要删除 --> 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" 5 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 7 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd 8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd 9 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"> 10 11 <!--指定Spring配置中用到的属性文件 --> 12 <bean id="propertyConfig" 13 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 14 <property name="locations"> 15 <list> 16 <value>classpath:jdbc.properties</value> 17 </list> 18 </property> 19 </bean> 20 <!-- JOTM实例 --> 21 <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" /> 22 <!-- JTA事务管理器 --> 23 <bean id="myJtaManager" 24 class="org.springframework.transaction.jta.JtaTransactionManager"> 25 <property name="userTransaction"> 26 <ref local="jotm" /> 27 </property> 28 </bean> 29 <!-- 数据源A --> 30 <bean id="dataSourceA" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" 31 destroy-method="shutdown"> 32 <property name="dataSource"> 33 <bean class="org.enhydra.jdbc.standard.StandardXADataSource" 34 destroy-method="shutdown"> 35 <property name="transactionManager" ref="jotm" /> 36 <property name="driverName" value="${jdbc.driver}" /> 37 <property name="url" value="${jdbc.url}" /> 38 </bean> 39 </property> 40 <property name="user" value="${jdbc.username}" /> 41 <property name="password" value="${jdbc.password}" /> 42 </bean> 43 <!-- 数据源B --> 44 <bean id="dataSourceB" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" 45 destroy-method="shutdown"> 46 <property name="dataSource"> 47 <bean class="org.enhydra.jdbc.standard.StandardXADataSource" 48 destroy-method="shutdown"> 49 <property name="transactionManager" ref="jotm" /> 50 <property name="driverName" value="${jdbc2.driver}" /> 51 <property name="url" value="${jdbc2.url}" /> 52 </bean> 53 </property> 54 <property name="user" value="${jdbc2.username}" /> 55 <property name="password" value="${jdbc.password}" /> 56 </bean> 57 <!-- 事务切面配置 --> 58 <aop:config> 59 <aop:pointcut id="serviceOperation" expression="execution(* *..service*..*(..))" /> 60 <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice" /> 61 </aop:config> 62 <!-- 通知配置 --> 63 <tx:advice id="txAdvice" transaction-manager="myJtaManager"> 64 <tx:attributes> 65 <tx:method name="delete*" rollback-for="Exception" /> 66 <tx:method name="save*" rollback-for="Exception" /> 67 <tx:method name="update*" rollback-for="Exception" /> 68 <tx:method name="*" read-only="true" rollback-for="Exception" /> 69 </tx:attributes> 70 </tx:advice> 71 72 <!--根据dataSourceA和sql-map-config_A.xml创建一个SqlMapClientA --> 73 <bean id="sqlMapClientA" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 74 <property name="dataSource"> 75 <ref local="dataSourceA" /> 76 </property> 77 <property name="configLocation"> 78 <value>sql-map-config_A.xml</value> 79 </property> 80 </bean> 81 <!--根据dataSourceB和sql-map-config_B.xml创建一个SqlMapClientB --> 82 <bean id="sqlMapClientB" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 83 <property name="dataSource"> 84 <ref local="dataSourceB" /> 85 </property> 86 <property name="configLocation"> 87 <value>sql-map-config_B.xml</value> 88 </property> 89 </bean> 90 <!--根据sqlMapClientA创建一个SqlMapClientTemplate的模版类实例sqlMapClientTemplateA --> 91 <bean id="sqlMapClientTemplateA" class="org.springframework.orm.ibatis.SqlMapClientTemplate"> 92 <property name="sqlMapClient" ref="sqlMapClientA" /> 93 </bean> 94 <!--根据sqlMapClientB创建一个SqlMapClientTemplate的模版类实例sqlMapClientTemplateB --> 95 <bean id="sqlMapClientTemplateB" class="org.springframework.orm.ibatis.SqlMapClientTemplate"> 96 <property name="sqlMapClient" ref="sqlMapClientB" /> 97 </bean> 98 99 <!-- 配置DAO,并注入所使用的sqlMapClientTemplate实例 --> 100 <bean id="tabADAO" class="com.lavasoft.stu.jtom.dao.impl.TabADAOImpl"> 101 <property name="sqlMapClientTemplate" ref="sqlMapClientTemplateA" /> 102 </bean> 103 <bean id="tabBDAO" class="com.lavasoft.stu.jtom.dao.impl.TabBDAOImpl"> 104 <property name="sqlMapClientTemplate" ref="sqlMapClientTemplateB" /> 105 </bean> 106 107 <!-- Service配置,注入DAO --> 108 <bean id="stuJotmService" class="com.lavasoft.stu.jtom.service.StuJotmServiceImpl"> 109 <property name="tabADAO" ref="tabADAO" /> 110 <property name="tabBDAO" ref="tabBDAO" /> 111 </bean> 112 </beans>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 jdbc.database=cms_release 2 jdbc.driver=com.mysql.jdbc.Driver 3 jdbc.url=jdbc:mysql://192.168.0.1:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull 4 jdbc.username=root 5 jdbc.password=123456 6 7 jdbc2.database=cms_release 8 jdbc2.driver=com.mysql.jdbc.Driver 9 jdbc2.url=jdbc:mysql://192.168.0.2:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull 10 jdbc2.username=root 11 jdbc2.password=123456
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE sqlMapConfig 3 PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" 4 "http://ibatis.apache.org/dtd/sql-map-config-2.dtd"> 5 6 <sqlMapConfig> 7 <settings cacheModelsEnabled="true" enhancementEnabled="true" 8 lazyLoadingEnabled="true" errorTracingEnabled="true" 9 useStatementNamespaces="true"/> 10 11 <sqlMap resource="com/lavasoft/stu/jtom/entity/sqlmap/TabA.xml"/> 12 13 </sqlMapConfig>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE sqlMapConfig 3 PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" 4 "http://ibatis.apache.org/dtd/sql-map-config-2.dtd"> 5 6 <sqlMapConfig> 7 <settings cacheModelsEnabled="true" enhancementEnabled="true" 8 lazyLoadingEnabled="true" errorTracingEnabled="true" 9 useStatementNamespaces="true"/> 10 11 <sqlMap resource="com/lavasoft/stu/jtom/entity/sqlmap/TabB.xml"/> 12 13 </sqlMapConfig>
1 public class Test { 2 private static ApplicationContext ctx = ApplicationContextUtil.getApplicationContext(); 3 private static StuJotmService ser = (StuJotmService) ctx.getBean("stuJotmService"); 4 5 public static void test_() { 6 TabA a = new TabA(); 7 a.setId(2L); 8 a.setName("aaa4"); 9 a.setAddress("address a4"); 10 11 TabB b = new TabB(); 12 b.setId(3L); 13 b.setName("bbb3"); 14 b.setAddress("address b5"); 15 16 ser.saveAB(a, b); 17 } 18 19 public static void main(String[] args) { 20 test_(); 21 } 22 }
2.3 测试效果
先运行Test文件,可以看到两个库两个表都正常插入值。
再把TabA的setId改为其他任意一个长整型,那么TabB肯定会报主键重复错,因为TabB的setId没改,那么如果事务生效的话,最后应该是两张表都没插入值,如果事务没生效,那么TabA会插入一条新的数据。
2.3 完整结构
完整代码下载: