spring的声明式事务

最近看了大量的spring关于DAO层和业务逻辑层的整合实现相关知识,有点混乱,趁此机会整理一下,让各个知识点更清晰,加深印象。


首先明确事务的几大特性:

(1)原子性,是指无论一次事务中包含几次数据库操作(CRUD),要么全部成功提交,要么全部失败回滚,如同原子一样看作是一次完整的操作。

(2)一致性,通俗的解释,事务前后的数据的状态需要保持一致,数据的最初状态和最终状态需要与预期的一致,保证不会出现中间状态。

(3)隔离性,是指当多个用户并发访问时,事务机制要保证为每一个用户单独开启一个事务,保证并发事务之前不会相互影响。

(4)持久性,是指当事务提交成功后,数据必须要持久化到数据库,不能出现事务提交成功,但是数据库中没有相应体现的这种状况。

这就是事物的ACID特性,任何一个系统的事务模型都需要满足上述特性,否则就会出现严重的数据问题。


再来了解一下,本地事务和全局事务的概念

本地事务,紧密依赖于本地数据库资源管理器(例如各种各样数据库连接池产品),这种事务就只局限于当前的数据库资源内,是单一的数据库资源事务。适用于简单的具有单一数据源的web系统。多数据源场景不适用。

本地事务的示例代码如下:

public void transferAccount() { 
        Connection conn = null; 
        Statement stmt = null; 
        try{ 
            conn = getDataSource().getConnection(); 
            // 将自动提交设置为 false,
            //若设置为 true 则数据库将会把每一次数据更新认定为一个事务并自动提交
            conn.setAutoCommit(false);
            
            stmt = conn.createStatement(); 
            // 将 A 账户中的金额减少 500 
            stmt.execute("\
            update t_account set amount = amount - 500 where account_id = 'A'");
            // 将 B 账户中的金额增加 500 
            stmt.execute("\
            update t_account set amount = amount + 500 where account_id = 'B'");
            
            // 提交事务
            conn.commit();
            // 事务提交:转账的两步操作同时成功
        } catch(SQLException sqle){            
            try{ 
                // 发生异常,回滚在本事务中的操做
               conn.rollback();
                // 事务回滚:转账的两步操作完全撤销
                stmt.close(); 
                conn.close(); 
            }catch(Exception ignore){ 
                
            } 
            sqle.printStackTrace(); 
        } 
    }

全局事务,系统中具有多个数据源,且每个数据源都有自己的数据库资源管理器,这就意味着在一次数据库事务中,会有横跨多个数据源进行操作的情况,例如订单系统和发货系统分属于不同的数据库中,一次成功的发货需要订单数+1,库存-1。

因此,全局事务管理器需要承担各个单一数据库资源管理器的协调工作,并保证全局事务也满足上述的ACID特性。




关于spring的声明式事务,需要先说一说什么是声明式事务。

DAO层的事务控制,通常是指对于数据库访问操作的事务处理,普通的实现方式即使用代码来控制事务的提交(commit)、回滚(rollback)等操作,这些代码若不使用框架进行整合,会造成DAO层和业务逻辑层的高耦合。由此spring提供了声明式事务的方案,即用声明式的事务代替代码式的事务。事务控制使用注解和配置文件声明等方式实现,其基本原理是使用AOP进行方法级别的事务控制,当然你也可以利用spring的规范自定义自己的代码式事务并使用AOP绑定自定义的声明式事务。

Spring内置了多种声明式事务的实现,如果不是特别复杂的业务,基本可以满足所有系统的需求


下面介绍几种常用的事务处理器:

1.DataSourceTransactionManager 单一数据源事务管理器,依赖于你的某一个datasource,提供对单个DataSource的事务管理。


2.JpaTransactionManager 这个相信使用过jpa的同学都不会陌生,用来对jpa的entityManagerFactory提供事务管理,集成jpa时的第一选择。


3.HibernateTransactionManager 用来对hibernate的某一个sessionFactory提供事务管理的支持,依赖hibernate3.x以上的版本。这个和楼上的jpa很像,我会在另一篇文章里讲讲jpa和hibernate的区别以及该如何选择。


4.JdoTransactionManager 这又是一个orm实现,个人感觉jdo和jpa没有什么大区别,又是sun搞的一套新标准而已。如果你的项目选择jdo作为你的项目的orm实现,那么这个事务管理器就是你的第一选择。


5.JtaTransactionManager 分布式事务管理器,对于使用了多数据源的项目来说,根据事务处理“All or nothing”的原则,这个事务管理器可以很好的解决不同数据源在一个事务中的协同问题。


6.WebLogicJtaTransactionManager WebLogic 8.1+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持。


重点介绍一下分布式事务管理器,

JtaTransactionManager要求所使用的datasource必须具有XA(二阶段提交)机制(例如innodb),比较流行的jta实现有Atomikos,bitronix.这两种实现的配置大同小异,最主要的数据库提交回滚操作还是会交给对应的datasource去实现,需要提供给spring的jtaTransactionManager接口两个userService的实现.下面给出参考配置代码,

在applicationContext.xml中:

    <!-- atomikos事务管理器 -->
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
        init-method="init" destroy-method="close">
        <description>UserTransactionManager</description>
        <property name="forceShutdown">
            <value>true</value>
        </property>
    </bean>

    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
        <property name="transactionTimeout" value="300" />
    </bean>
 <!-- spring 事务管理器 -->
    <bean id="springTransactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager">
            <ref bean="atomikosTransactionManager" />
        </property>
        <property name="userTransaction">
            <ref bean="atomikosUserTransaction" />
        </property>
    </bean>

参考:Spring的jta配置


需要提供给spring的jtaTransactionManager两个接口的实现,这两个接口分别是transacationManager和userTransacation。

按照原理来说,transacationManager是提供给服务商实现的接口,用于创建用户的事务委托并将具体操作委托给单一的transacation对象进行操作,同时事务的隔离性也在transacationManager中得到了体现,它需要为每一个用户维护一个线程安全的transacation对象,因此全局事务开始时,它会创建一个transacation对象,并用threadlocal来维护线程安全。

而userTransaction是提供给用户使用的接口,用户可以在begin(),commit()等方法中实现自己的业务操作,但是最终的事务操作会由transacationManager委托给transaction对象来执行。

实际上,通过上述代码的配置与spring声明式事务的使用,spring就会自动在被@Transacational注解过的类中的所有方法上创建环绕通知,如在方法开始调用事务对象的begin()方法,将方法中的所有数据库操作管理到全局事务中。用户其实无须将事务管理器与数据库资源(datasource)做强行关联,在任意一个crud会话执行时,支持XA协议的datasource会实现一个方法,该方法会自动查询当前数据连接资源是否包含在事务管理中,若不存在则加入,因此也可以看出,全局事务中各个数据源所使用的datasource必须为XAdatasource。


参考:JTA实现原理



关于分布式事务XA规范,有几点需要说明:

1.无论使用二阶段提交(2PC)或是三阶段提交(3PC),都存在数据一致性的问题。当作为协调者的spring挂掉之后,2PC协议下,多个数据源的锁定资源会一直阻塞,得不到释放,导致服务器其他应用也无法访问该资源,3PC协议虽然通过使用超时,强制多数据源超时后自动提交,但是若协调者发送的是回滚消息,而数据源执行的却是提交操作,则也会引发一系列数据一致性问题。


2.不是所有的业务都适合使用分布式事务的XA规范。它的同步阻塞机制在高并发访问下很容易造成服务器卡死状态。

参考网上一篇较为详细的文章:分布式事务XA规范




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值