Java事务(七) - 分布式事务 - spring + JTA + jotm

一. 前言:

在写这篇博客之前,我们需要弄清楚两个概念:本地事务和分布式事务。

本地事务:只处理单一数据源,比如单个数据库。

分布式事务:处理多种异构的数据源, 比如某个业务操作中同时包含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

时间: 2024-12-24 04:18:31

Java事务(七) - 分布式事务 - spring + JTA + jotm的相关文章

Java事务处理全解析(八)——分布式事务入门例子(Spring+JTA+Atomikos+Hibernate+JMS)

在本系列先前的文章中,我们主要讲解了JDBC对本地事务的处理,本篇文章将讲到一个分布式事务的例子. 请通过以下方式下载github源代码: git clone https://github.com/davenkin/jta-atomikos-hibernate-activemq.git 本地事务和分布式事务的区别在于:本地事务只用于处理单一数据源事务(比如单个数据库),分布式事务可以处理多种异构的数据源,比如某个业务操作中同时包含了JDBC和JMS或者某个操作需要访问多个不同的数据库. Java

从本地事务到分布式事务到微服务下事务

从本地事务到分布式事务到微服务下事务 一.传统本地事务 传统单服务器,单关系型数据库下事务比较简单,完全可用很简单的实现ACID,实际中我们实现一个业务时只需要:开启一个事务-操作数据库-提交/回滚这个事务,这样就完美的实现了一次事务操作,更简单点我们通常会通过spring集成事务直接指定在哪些服务什么样的方法执行什么样的事务即可,更甚至我们业务实现基本都忽略了事务,具体图如下: 二.传统分布式事务 在传统一服务,一个关系数据库架构基础上,随着访问量的增大,单机很明显已满足不了现状,于是我们顺其

Java事务(八) - 分布式事务 - spring + JTA + atomikos

一. 前言: 上一篇博客中,我们使用jotm实现了分布式事务, 本篇将使用atomikos实现. 基本的代码都是一样的,就是配置略有不同. 二. 代码实现: 1. 代码结构图: 2. 配置文件:ApplicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"

设计----【分布式事务】分布式事务和解决方案

一.前言 分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免,本文就分布式事务来简单聊一下. 二.数据库事务 在说分布式事务之前,我们先从数据库事务说起. 数据库事务可能大家都很熟悉,在开发过程中也会经常使用到.但是即使如此,可能对于一些细节问题,很多人仍然不清楚.比如很多人都知道数据库事务的几个特性:原子性(Atomicity ).一致性( Consistency ).隔离性或独立性( Isolation)和持久性(

【分布式事务】分布式事务解决方案

一.第一种方案:能不用分布式事务就不用 明确系统是否真的需要分布式事务: 因为不论任何一种分布式解决方案都会增加你系统的复杂度,这样的成本还是挺高的,千万不要因为追求某些设计,而引入不必要的成本和复杂度. 二.第二种方案:XA 分布式事务 (MySQL是支持XA事务的) 属于2PC:XA是由X/Open组织提出的分布式事务的规范. X/Open DTP(X/Open Distributed Transaction Processing Reference Model) 是X/Open 这个组织定

本地事务和分布式事务工作实践 【转】

一:从事务的历史说起 知已知彼,百战不败.想了解事务,我们从事务的历史说起. 在Windows平台上,事务的概念最开始出现在关系型数据库中,但是随着.net平台的发展,事务包括的的范围也越来越宽,先一睹为快, 在关系型数据库中的事务是通过begin transaction,rollback transaction, commit 等关键字来实现事务的. BEGIN TRANSACTION  UPDATE [dbo].[T_ACCOUNT] SET BALANCE = BALANCE + @amo

本地事务和分布式事务工作实践

一:从事务的历史说起 知已知彼,百战不败.想了解事务,我们从事务的历史说起. 在Windows平台上,事务的概念最开始出现在关系型数据库中,但是随着.net平台的发展,事务包括的的范围也越来越宽,先一睹为快, 在关系型数据库中的事务是通过begin transaction,rollback transaction, commit 等关键字来实现事务的. BEGIN TRANSACTION  UPDATE [dbo].[T_ACCOUNT] SET BALANCE = BALANCE + @amo

本地事务和分布式事务工作实践 [转]

一:从事务的历史说起 知已知彼,百战不败.想了解事务,我们从事务的历史说起. 在Windows平台上,事务的概念最开始出现在关系型数据库中,但是随着.net平台的发展,事务包括的的范围也越来越宽,先一睹为快, 在关系型数据库中的事务是通过begin transaction,rollback transaction, commit 等关键字来实现事务的. BEGIN TRANSACTION  UPDATE [dbo].[T_ACCOUNT] SET BALANCE = BALANCE + @amo

springboot(整合事务和分布式事务)

springboot +mybatis 单数据源,事务 事务:简单理解指的是一组操作,里面包含许多个单一的逻辑,只要有一个逻辑没有执行成功 ,那么都算失败.所有的数据都回归到最初的状态(回滚) 代码实例: @RequestMapping("/testTranSctional") @ResponseBody public void testTranSctional(String name, Integer age) { userMapperTest01.insert(name, age)