导致@Transactional 失效的几个原因


前言

这里要说明下,以下五点并不是我自己总结出来的,而是参考了网上众多大佬解答。
我自己仅是用代码写了个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 事务生效
    @Autowired
    private ApplicationContext applicationContext;
    @Override
    public 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)获取代理
  
 @Override
 public 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; 
    });
} 

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

总结

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

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值