当前位置: 代码迷 >> 综合 >> 导致@Transactional 失效的几个原因
  详细解决方案

导致@Transactional 失效的几个原因

热度:21   发布时间:2023-11-22 07:40:56.0

@Transactional 失效的原因

  • 前言
  • 一、同一个类里的方法调用
  • 二、方法不是public修饰
  • 三、方法中使用try catch
  • 四、rollbackFor设置原因
    • 1、rollbackFor默认是捕获RunTimeException,其他异常不会回滚
    • 2、propagation配置错误 这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。
  • 五、数据库引擎不支持事务
  • 扩展 :避免长事务
    • 1、何为长事务?
    • 2、避免长事务
  • 总结


前言

这里要说明下,以下五点并不是我自己总结出来的,而是参考了网上众多大佬解答。
我自己仅是用代码写了个demo,验证了一下,并记录下来以便工作学习之用。


一、同一个类里的方法调用

原因:

spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所以就不会启动transaction,我们看到的现象就是@Transactional注解无效。

解决方法一,代码如下:

@Override@Transactional(rollbackFor = Exception.class)public void testTraOne(UserDemoEntity model) throws Exception {
    this.save(model);throw new Exception();}//从上下文中获取bean 事务生效@Autowiredprivate ApplicationContext applicationContext;@Overridepublic void testTraThree(UserDemoEntity model) throws Exception {
    UserDemoServiceImpl impl =  applicationContext.getBean(UserDemoServiceImpl.class);impl.testTraOne(model);}

解决方法二:
启动类添加@EnableAspectJAutoProxy(exposeProxy = true),方法内使用AopContext.currentProxy()获得代理类,使用事务。

//启动类上(SpringBootApplication.java)加上注解@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class SpringBootApplication {
    //xxx
}//实现类(UserDemoServiceImpl)获取代理@Overridepublic void testTraThree(UserDemoEntity model) throws Exception {
    UserDemoServiceImpl impl =  (UserDemoServiceImpl)AopContext.currentProxy();;impl.testTraOne(model);}

二、方法不是public修饰

computeTransactionAttribute方法在获取Transactional注解信息时, 会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息
备注:在idea的语法检查中就会报错,所以这点…

三、方法中使用try catch

如果catch了异常 处理完没有抛出 那么也会导致事务失效
解决方法:catch处理了之后,再throw 出去…(用了catch又throw,感觉多此一举呢)
解决代码如下:

    @Override@Transactional(rollbackFor = Exception.class)public void testTraFour(UserDemoEntity model) throws Exception {
    try {
    this.save(model);throw new Exception("手动抛出异常");}catch (Exception e){
    log.error("手动抛出异常");throw new Exception("手动抛出异常");}}

四、rollbackFor设置原因

1、rollbackFor默认是捕获RunTimeException,其他异常不会回滚

解决方法: @Transactional(rollbackFor = Exception.class) 自定义什么异常会回滚

2、propagation配置错误 这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。

TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

五、数据库引擎不支持事务

如:mysql的 innodb支持 但是myisam不支持


扩展 :避免长事务

1、何为长事务?

顾名思义就是运行时间比较长,长时间未提交的事务,也可以称之为大事务 。

长事务引发的常见危害有:
1:数据库连接池被占满,应用无法获取连接资源;
2:容易引发数据库死锁;
3:数据库回滚时间长;
4:在主从架构中会导致主从延时变大。

2、避免长事务

解决方法一:手动管理事务的开启、提交、回滚等操作,使用TransactionTemplate 。

@Autowired 
private TransactionTemplate transactionTemplate; ... public void save(RequestBill requestBill) {
     transactionTemplate.execute(transactionStatus -> {
    requestBillDao.save(requestBill);//保存明细表requestDetailDao.save(requestBill.getDetail());return Boolean.TRUE; });
} 

解决方法二:对方法进行拆分,将不需要事务管理的逻辑与事务操作分开 。
要注意避免发生第一点:同一个类里的方法调用。

总结

暂时从网上总结到以上几点,后续有收集到新的原因,也会更新到这里。

  相关解决方案