摘要:Spring事务失效的场景
1.Spring 框架配置问题
1. service类没有被Spring管理
1 | //@Service (注释了@Service) |
上面例子中,Spring事务没有生效,因为Spring事务是由AOP机制实现的,也就是从Spring IOC 容器中获取bean 时,Spring 会为目标类创建代理,来支持事务的。但是@Service被注释后,service类都不是被Spring 管理的,更不会创建代理来支持事务。
2、没有在Spring配置中启用事务管理器
1 |
|
没有配置事务管理器,Spring无法创建事务代理对象,导致事务不生效。
解决方法
1 |
|
如果是SpringBoot项目,默认会自动配置事务管理器并开启事务支持的。
2. Spring AOP 代理问题
1.事务方法被final、static关键字修饰
1 | /** |
事务不生效的原因:如果一个方法被声明为final或者static,则该方法不能被子类重写,也就是说无法在该方法上进行动态代理,这会导致Spring无法生成事务代理对象来管理事务。
2.同一个类中,方法内部调用
1 | /** |
事务不生效的原因: 事务是通过Spring AOP代理来实现的,而在同一个类中,一个方法调用另一个方法时,调用方法直接调用目标方法的代码,而不是通过代理类进行调用。调用目标方法不是通过代理类进行的,因此事务不生效。
解决方案:可以新建多一个类,让这两个方法分开,分别在不同的类中,也可以在该 Service 类中注入自己,或者通过AopContext.currentProxy()获取代理对象。
3.方法的访问权限不是public
1 | /** |
事务不生效的原因:Spring事务是由AOP机制实现的,AOP机制的本质是动态代理,而代理的事务方法不是public的话,AbstractFallbackTransactionAttributeSource.computeTransactionAttribute()就会返回null,也就是这时事务属性不存在了。
3.数据库不支持
数据库的存储引擎不支持事务
Spring事务的底层,还是依赖于数据库本身的事务支持。在MySQL中,MyISAM存储引擎是不支持事务的,InnoDB引擎才支持事务。因此开发阶段设计表的时候,确认你的选择的存储引擎是支持事务的。
4.@Transactional配置问题
readOnly=true
事务超时时间设置过短 timeout
使用了错误的事务传播机制 propagation =Propagation.NOT_SUPPORTED传播特性不支持事务。
rollbackFor属性配置错误 rollbackFor属性指定的异常必须是Throwable或者其子类。
5.开发使用不当
1.事务注解被覆盖导致事务失效
2.嵌套事务
3.事务多线程调用
1 | /** |
事务失效原因:这是因为Spring事务是基于线程绑定的,每个线程都有自己的事务上下文,而多线程环境下可能会存在多个线程共享同一个事务上下文的情况,导致事务失效。Spring事务管理器通过使用线程本地变量(ThreadLocal)来实现线程安全。
在Spring事务管理器中,通过TransactionSynchronizationManager类来管理事务上下文。TransactionSynchronizationManager内部维护了一个ThreadLocal对象,用来存储当前线程的事务上下文。在事务开始时,TransactionSynchronizationManager会将事务上下文绑定到当前线程的ThreadLocal对象中,当事务结束时,TransactionSynchronizationManager会将事务上下文从ThreadLocal对象中移除。
4.异常被捕获并处理了,没有重新抛出
1 | /** |
事务不生效的原因: 事务中的异常已经被业务代码捕获并处理,而没有被正确地传播回事务管理器,事务将无法回滚。我们可以从Spring源码(TransactionAspectSupport这个类)中找到答案.在invokeWithinTransaction方法中,当Spring catch到Throwable异常的时候,就会调用completeTransactionAfterThrowing()方法进行事务回滚的逻辑。
解决方案:
1.在Spring事务方法中,当我们使用了try-catch,如果catch住异常,记录完异常日志什么的,一定要重新把异常抛出来.
2.手动回滚事物 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
5.手动抛了别的异常
1 | /** |
事务不生效的原因:上面的代码例子中,手动抛了Exception异常,但是是不会回滚的,因为Spring默认只处理RuntimeException和Error,对于普通的Exception不会回滚,除非,用rollbackFor属性指定配置。
解决方案:**添加属性配置@Transactional(rollbackFor = Exception.class)**。