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;
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
│ 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
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
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 >
<!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 >
<!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: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);
}
}
* 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);
}
* 测试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;
}
}
* 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!");
}
}
}
* 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
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 >
<!-- 局部单元测试使用,不正式发布,不要删除 -->
< 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
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 >
<!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 >
<!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
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_();
}
}
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中可以拷贝直接用