事务方法调用非事务方法_Spring事务失效原因及解决方法

spring 提供事务功能需要依赖于数据库的事务支持。而数据库层的事务提交和回滚是通过bin log 或者 redo log 实现的。大部分情况下我们使用基于 @Transactional 注解的声明式事务。声明式事务本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

对于Spring事务来说,在以下9种情况下会出现事务失效的情况。

1、数据库本身不支持事务

以 MySQL 为例,MyISAM 引擎是不支持事务操作的,InnoDB ,要支持事务都会使用 InnoDB引擎。

解决方法:使用支持事务的数据库引擎。

2、事务没有被Spring管理

c64e23a6c15ae267aa42b9bb981cde14.png

这里把 @Service 注解注释掉,这个类就不会被Spring加载成一个 Bean,此时这个类没有被 Spring 管理了,Spring事务自然就失效了。

解决方法:包含的事务的类通过@service、@component等注解修饰即可。

3、同一类中的自身方法调用

359411417e50f34bc68ef6025310c1aa.png

因为spring声明式事物是基于AOP实现的,是使用动态代理来达到事物管理的目的,当前类调用的方法上面加@Transactional 这个是没有任何作用的,因为调用这个方法的是this,没有经过 Spring 的代理类。

解决方法:1)在声明一个service,将内部调用改为外部调用。

82903f66fdc8f48a67137e6fa55e1c52.png

0038668beccaccb9a5e3424fee2a4dbb.png

2) 使用编程式事务.

c9d69b1c5afd3388ddd1d12703b388fc.png

4、方法不是Public

8f01610dde03d8d458ae78152a33d33a.png

Spring官方文档中有明确的说明,@Transactional 只有修饰public方法时才会生效,修饰protected、private或者包内可见的方法均不会生效。

解决方法:@Transactional 修饰public方法即可,或者@Transactional 修饰类,需要事务生效的方法都为public。

5、事物传播性问题

3240e3782c29809f823bf782466995a8.png

Spring 事务的传播行为定义如下:

  • PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
  • PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行
  • PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按 REQUIRED 属性执行

当传播行为设置了PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER,PROPAGATION_SUPPORTS这三种时,就有可能存在事物不生效。

解决方法:尽量避免使用上面三种传播行为。

6、数据源未配置事务管理器

9ba0e9c3b3f3b982d6ce09bfe44e008c.png

如上面所示,当前数据源若没有配置事务管理器,那么事务是不会生效的。

解决方法:有配置事务管理器。

7、使用cglib代理

要注意的是单纯使用cglib代理是不会出现事务失效的情况,只有当接口层使用声明式事务同时使用了cglib代理才会出现事务失效的情况。如下图为接口层使用声明式事务示例。

83231c91f691ec8d72f499fb497aba3d.png

解决方法:cglib代理和接口层声明式事务二者不同时使用。

8、rollbackFor异常指定错误

3f4d2f0671cd96a6376e5fb77fd13cbd.png

例如rollbaclFor指定的是DataFormatException,但程序没有抛DataFormatException,因此事务不会回滚。若没有指定回滚异常,默认的回滚异常是RuntimeException ,如果出现其他异常那么就不会回滚事物。

解决方法:明确需要回滚的异常,正确设置rollbackFor的异常class。

9、异常被catch住了

代码中手动catch了异常,然后又未抛出来,此时事务就不生效了。

解决方法:要么不catch需要回滚的异常,要么catch之后再抛出。

61c0cfee04061a5dc2e6bf1c89b4225c.png

tips:文中代码仅为示例。


版权声明:本文为weixin_36393718原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。