事务注解@Transactional不生效? 为啥

文章详细介绍了@Transactional注解在Spring中的事务管理,包括默认的回滚规则,事务失效的常见场景,如非public方法、final修饰、内部调用、未被Spring管理等,并提供了相应的解决方案。此外,还提到了事务未回滚的原因,如错误的传播特性、异常处理不当,并给出了使用事务的建议,强调了明确事务范围和避免滥用事务的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://blog.youkuaiyun.com/minghao0508/article/details/124374637 文章源地址

3.3 事务回滚

@Transactional默认只能回滚RuntimeException和RuntimeException下面的子类抛出的异常,不能回滚Exception异常;如果需要支持回滚Exception异常,需要显示的指明,如@Transactional(rollbackFor = Exception.class);

3.4 失效场景

介绍几种常见的事务实效的场景,这里引用一张图来说明;

在这里插入图片描述

3.4.1 打了@Transactional但是事务不生效

(1)@Transactional注解未打在public方法上

Java的访问权限主要有四种:private、default、protected、public;如果事务方法定义了错误的访问权限(非public方法),会导致事务失效;

(2)目标方法用final修饰

某个方法不想被子类重写,可以将该方法定义成final的;如果将事务方法定义成final,会导致事务失效;

原因:Spring事务基于Spring AOP,通过JDK动态代理或者CGlib代理,在代理类中实现的事务功能;但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法;同样,static修饰的方法,同样无法通过动态代理,变成事务方法;

(3)同一个类中的方法直接内部调用

原因:方法被事务管理是因为Apring AOP为其生成代理了对象,但是直接this调用同类方法,调用的是目标类对象的方法,而非代理类方法,因此,在同类中的方法直接内部调用,会导致事务失效;

如果有些场景,确实想在同一个类的某个方法中,调用当前类的另外一个事务方法,该怎么办呢?

方法1:新写一个Service,把事务方法挪过去,在当前类注入新的Service

方法2:在当前Service注入自己;可能有些人可能会有这样的疑问:这种做法会不会出现循环依赖问题?不会,Spring的Bean初始化流程中使用三级缓存解决循环依赖问题,可参考我的文章《Spring——循环依赖&三级缓存【建议收藏】》;

方法3:在当前Service类中使用AopContext.currentProxy()获取当前类的代理对象,相比方法2更加直观;代码示例如下:

@Servcie
public class ServiceA {
 
   public void save(User user) {
         queryData1();
         queryData2();
         ((ServiceA)AopContext.currentProxy()).doSave(user);
   }
 
   @Transactional(rollbackFor=Exception.class)
   public void doSave(User user) {
       addData1();
       updateData2();
    }
 }

(4)事务方法所在的类未被Spring管理

使用Spring事务的前提是:对象要被Spring IOC容器管理,需要创建bean实例;打了注解,但是忘了在当前类加@Service注解,导致事务不生效,也是小白常见的编码错误;

(5)多线程调用

如果两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务;如果看过Spring事务源码,能会知道Spring的事务是通过数据库连接Connection来实现的;当前线程中保存了一个map,key是数据源,value是数据库连接;
在这里插入图片描述
我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚;如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务;

(6)存储引擎不支持事务

如MYSQL的myisam存储引擎不支持事务,有些老项目中,可能还在用它;在开发的过程中,如果发现某张表的事务一直都没有生效,可以检查下那张表的存储引擎,看是否支持事务;

(7)未开启事务

这个原因极其容易被忽略;

如果你使用的是Springboot项目,那么你很幸运,因为Springboot通过DataSourceTransactionManagerAutoConfiguration类,已经默默的帮你开启了事务,只需要配置数据源spring.datasource相关参数即可;

但如果你使用的还是传统的老Spring项目,则需要在applicationContext.xml文件中,手动配置事务相关参数;

3.4.2 事务未回滚

(1)使用了错误的传播特性,如新开启了一个事物,可能导致新事物和原事务不会一起回滚;

(2)自己吞了异常,忘记抛出了;或者抛出了非运行时异常,但又没有配置到注解上;因为Spring事务默认情况下只会回滚RuntimeException(运行时异常)和Error(错误);

3.5 使用建议

  1. 要知道@Transactional注解里面每个属性的含义,@Transactional注解属性就是来控制事务属性的,通过这些属性来生成事务;

  2. 要明确我们添加的@Transactional注解会不会起作用;如@Transactional注解在外部调用的函数上才有效果,直接内部调用无效;

  3. 显示的指定rollbackFor注解属性,即使rollbackFor有默认值,但阿里巴巴开发者规范中,还是要求开发者重新指定该参数,因为如果使用默认值,一旦程序抛出了非运行时的其他Exception,事务不会回滚,这会出现很大的bug;

  4. 要明确事务的作用范围,有@Transactional的函数调用有@Transactional的函数的时候,进入第二个函数的时候是新的事务,还是沿用之前的事务;稍不注意就会抛UnexpectedRollbackException异常;

  5. 不要滥用事务,避免大事务,事务会影响数据库的读写性能,非必要场景不建议使用;适当的对方法里面的实务操作拆分执行;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值