zoukankan      html  css  js  c++  java
  • spring事务使用心得

    Spring支持两种类型的事务管理:

    1. 编程式事务管理: 直接使用PlatformTransactionManager实现或使用TransactionTemplate模板类
    2. 声明式事务管理: 这意味着你的业务代码将于事务管理分开,只用注解或基于XML配置来管理事务

    自从有了基于aop的事务注解,事务的使用变得更简单,相信大家都喜欢这货。够轻、够好用,哪里需要事务只需要一个注解即可,可以在类或者是方法上使用它。确实它足够好用,不过还是有其不足的地方,这个稍后再探讨。我们先看下spring的事务传播行为类型:

    事务传播行为类型

    事务传播行为类型

    说明

    PROPAGATION_REQUIRED

    如果当前没有事务,就新建一个事务。如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

    PROPAGATION_REQUIRES_NEW

    如果当前没有事务,就新建一个事务。如果当前存在事务,就把当前事务挂起,另建一个事务。

    PROPAGATION_NESTED

    如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。外套的事务异常可使内嵌事务回滚,反之不会。(底层的数据源必须基于 JDBC 3.0 ,并且实现者需要支持保存点事务机制)

    PROPAGATION_SUPPORTS

    支持当前事务,如果当前没有事务,就以非事务方式执行。

    PROPAGATION_NOT_SUPPORTED

    以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

    PROPAGATION_NEVER

    以非事务方式执行,如果当前存在事务,则抛出异常。

    PROPAGATION_MANDATORY

    使用当前的事务,如果当前没有事务,就抛出异常。

    readOnly
    事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示 。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。

    Timeout

    在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。

    隔离级别的相关内容可以参考我的另一篇文章数据库事务【隔离级别】

    其中,spring的默认事务传播方式和隔离级别分别是PROPAGATION_REQUIRED和ISOLATION_DEFAULT(使用数据库默认的事务隔离级别),readOnly为false。

     

    好了,上面基本介绍完了spring的相关事务配置参数,我来讲讲我在项目中碰到的一个奇怪的问题,不知你碰到没,就是spring事务失效了。一般来说我们只要在类或相应的方法上设置了@Transactional,那便可以使用上spring事务了,我一开始也是这么认为的,我写了两个方法,serviceA.methodA没加事务,serviceA.methodB加了事务注解,其中methodA调用了methodB,我让Controller调用了serviceA.methodA,然后故意让methodB抛出了异常,咦,结果并不如预期设想,事务没有回滚。我一开始还以为是事务的传播类型出了差错,但是变换了始终没有效果。之前一直都是这么使用的啊,也测试过。于是,我查了之前的代码,发现跟这次写的代码的不同之处在于之前Controller调用的方法上有加了事务,或者是serviceA.methodA(事务)调用了serviceB.methodA(事务)。那我就在想会不会是aop拦截出的问题,于是上网找了许久,终于找到问题的原因,现在我们分析下:

     

    Spring的AOP实现方式有两种:1、Java代理方式;2、Cglib动态增强方式,这两种方式在Spring中是可以无缝自由切换的。Java代理方式的优点是不依赖第三方jar包,缺点是不能代理类,只能代理接口。

          Spring通过AopProxy接口,抽象了这两种实现,实现了一致的AOP方式:

    现在看来,这种抽象同样带了一个缺陷,那就是抹杀了Cglib能够直接创建普通类的增强子类的能力,Spring相当于把Cglib动态生成的子类,当普通的代理类了,这也是为什么会创建两个对象的原因。下图显示了Spring的AOP代理类的实际调用过程:

    因此,从上面的分析可以看出,methodB没有被AopProxy通知到,导致最终结果是:被Spring的AOP增强的类,在同一个类的内部方法调用时,其被调用方法上的增强通知将不起作用。

    而这种结果,会造成的影响有:

          1:内部调用时,被调用方法的事务声明将不起作用

          2:换句话说,你在某个方法上声明它需要事务的时候,如果这个类还有其他开发者,你将不能保证这个方法真的会在事务环境中

          3:再换句话说,Spring的事务传播策略在内部方法调用时将不起作用。不管你希望某个方法需要单独事务,是RequiresNew,还是要嵌套事务,要Nested,等等,统统不起作用。

          4:不仅仅是事务通知,所有你自己利用Spring实现的AOP通知,都会受到同样限制。。。。

    知道了原因,问题就迎刃而解了,你可以使用以下方式来避免这个缺陷:

    1、分开两个方法调用,即serviceA.methodA不配置事务,调用配置上事务的serviceB.methodA

    2、方法内使用编程式事务

    参考自:http://blog.csdn.net/seelye/article/details/40144817

  • 相关阅读:
    Linux学习之路:shell变量(二)环境变量
    LVS配置与安装
    Linux学习之路:shell变量(一)
    SQL Server用存储过程新建视图
    【转】SQL Server 2012 配置AlwaysOn(三)
    【转】SQL 语句:Alter table
    Linux学习之路:认识shell和bash
    【转】Centos配置yum源
    【Azure 应用服务】App Service 无法连接到Azure MySQL服务,报错:com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
    【Azure API 管理】在APIM 中添加 logtoeventhub 策略,把 Request Body 信息全部记录在Event Hub中
  • 原文地址:https://www.cnblogs.com/zhuanghuang/p/5515830.html
Copyright © 2011-2022 走看看