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

posted @ 2016-05-21 22:48  圣雄1990  阅读(1856)  评论(0编辑  收藏  举报