一. 前言:
在写这篇博客之前,我们需要弄清楚两个概念:本地事务和分布式事务。
本地事务:只处理单一数据源,比如单个数据库。
分布式事务:处理多种异构的数据源, 比如某个业务操作中同时包含JDBC和JMS或者某个操作需要访问多个不同的数据库。
Java通过JTA完成分布式事务, JTA本身只是一种规范, 本篇博客将使用JOTM作为实现, 后续还会使用Atomikos实现。
二. 业务背景:
假定我们有这样一个需求:当我们新建一个用户的时候需要往一个DB中插入一条用户记录,还需要往另一个DB中记录日志
因为是不同的DB操作,所以这里就涉及到分布式事务的处理。
三. 代码实现:
1. 代码结构图:
2. 建表语句:
create database log;
DROP TABLE IF EXISTS `log`;
CREATE TABLE `log` (
`id` varchar(20) NOT NULL,
`content` varchar(100) default NULL,
PRIMARY KEY (`id`)
);
create database user;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` varchar(20) NOT NULL,
`name` varchar(40) default NULL,
PRIMARY KEY (`id`)
);
3. 配置文件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: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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 引用Spring内部所提供的对JOTM支持的工厂类 -->
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />
<!-- 配置JTA事务管理器, 并在管理器中使用上面所配置的JOTM -->
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransaction" ref="jotm" />
</bean>
<!-- 配置多个数据源 -->
<bean id="db1" 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="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:MySQL://localhost:3306/user" />
</bean>
</property>
<property name="user" value="root" />
<property name="password" value="root" />
</bean>
<bean id="db2" 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="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:MySQL://localhost:3306/log" />
</bean>
</property>
<property name="user" value="root" />
<property name="password" value="root" />
</bean>
<!-- 根据不同的数据源配置两个jdbcTemplate -->
<bean id="jdbcTemplate1" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="db1" />
</bean>
<bean id="jdbcTemplate2" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="db2" />
</bean>
<bean id="userDao" class="com.zdp.dao.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplate1" />
</bean>
<bean id="logDao" class="com.zdp.dao.LogDao">
<property name="jdbcTemplate" ref="jdbcTemplate2" />
</bean>
<bean id="userService" class="com.zdp.service.UserService">
<property name="userDao" ref="userDao" />
<property name="logDao" ref="logDao" />
</bean>
<!-- JTA事务传播特性 -->
<tx:advice id="txAdviceJTA" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="create*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="del*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor pointcut="execution(* com.zdp.service..*(..))" advice-ref="txAdviceJTA" />
</aop:config>
</beans>
4. service业务类:
public class UserService {
private UserDao userDao;
private LogDao logDao;
public void saveUser(String id, String name) {
userDao.insertUser(id, name);
// int i = 1 / 0; // 制造异常
logDao.insertLog(id, id + "_" + name);
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public LogDao getLogDao() {
return logDao;
}
public void setLogDao(LogDao logDao) {
this.logDao = logDao;
}
}
5. Dao类:
public class UserDao extends JdbcDaoSupport {
public void insertUser(String id, String name) {
JdbcTemplate template = getJdbcTemplate();
template.execute("insert into user values('" + id + "','" + name + "')");
}
}
public class LogDao extends JdbcDaoSupport {
public void insertLog(String id, String content) {
JdbcTemplate template = getJdbcTemplate();
template.execute("insert into log values('" + id + "','" + content + "')");
}
}
6. 测试类:
public class UserTest {
@Test
public void testSave() {
ApplicationContext cxt = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserService us = (UserService) cxt.getBean("userService");
us.saveUser("1", "zhangsan");
}
}
源码下载: http://download.csdn.net/detail/zdp072/7950383