我总结的几种@Transactional失效原因说明

 更新时间:2022年11月15日 15:53:49   作者:滕青山YYDS  
这篇文章主要是我总结的几种@Transactional失效原因说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

总结几种@Transactional失效原因

非public方法

spring事务是通过动态代理的方法来实现的,有两种实现动态代理的方式,jdk动态代理方式是将目标对象放入代理对象内部,通过代理对象来访问目标对象;cglib字节码生成是通过生成目标对象的子类,通过重写的方式来完成对父类的增强。

但是它俩实际上可以为非public方法生成代理对象,只不过spring在调用动态代理之前,会过滤掉非public方法。如果修改spring的源码就可以为非public方法生成代理对象了。

自调用问题

若同一类中的没有@Transactional注解的方法内部调用有@Transactional注解的方法,那么该事务会被忽略。

原因是Spring事务是通过Spring AOP完成的,如果从外部直接访问使用@Transactional注解的方法,那么spring实际上会调用代理对象上的方法,在代理对象中完成事务的逻辑;

但是如果从目标对象内部调用了使用@Transactional注解的方法,比如在method1方法中调用了method2方法,method1没有加@Transactional 注解,就算method2加了@Transactional 注解也没用。因为这时会直接调用目标对象中的method1方法,进而再调用目标对象的method2方法,并没有走代理对象导致代理失效。

异常相关问题

内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚:

原因是spring事务源码中是通过有没有出现异常来判断是否回滚的。

抛出非运行时异常

所以最好给@Transactional添加上rollbackFor=Exception.class

传播机制配置错误

错误地使用传播机制也会导致事务失效。如果使用了NOT_SUPPORTED和NEVER传播机制,那么事务机会失效,如果使用了SUPPORTS传播机制并且当前不存在事务那么事务也会失效。

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

@Transactional事务失效场景类内部调用实测

环境springboot2.7,mysql5.7

demo1

@Component
@Order(6)
@Slf4j
public class TestRunner implements ApplicationRunner {
 
    @Autowired
    TestService testService;
 
    @Override
    public void run(ApplicationArguments args) throws Exception {
        testService.insertAndUpdate();
    }
}
@Service("testService")
public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements TestService {
 
    @Override
    public boolean updateByIdOne() {
        LambdaUpdateWrapper<Test> lambdaUpdateWrapper = new UpdateWrapper<Test>().lambda();
        lambdaUpdateWrapper.eq(Test::getId,1);
        lambdaUpdateWrapper.set(Test::getName,"test");
 
        return this.update(lambdaUpdateWrapper);
    }
 
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean insertAndUpdate() {
        boolean b = this.updateByIdOne();
 
 
        Test test = new Test();
        test.setName("2222");
 
 
        boolean save = this.save(test);
        if(b){
            throw new RuntimeException("ts");
        }
        return save;
    }
}

以上代码先跑一遍,看看抛出异常情况,能不能回滚

看库  毫无变化

看主键递增量其实是插入过了,我觉得事务还是生效了

demo2

@Service("testService")
public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements TestService {
 
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean updateByIdOne() {
        LambdaUpdateWrapper<Test> lambdaUpdateWrapper = new UpdateWrapper<Test>().lambda();
        lambdaUpdateWrapper.eq(Test::getId,1);
        lambdaUpdateWrapper.set(Test::getName,"test");
        boolean update = this.update(lambdaUpdateWrapper);
 
        if(update){
            throw new RuntimeException("updateByIdOne");
        }
 
        LambdaUpdateWrapper<Test> lambdaUpdateWrapper2 = new UpdateWrapper<Test>().lambda();
        lambdaUpdateWrapper2.eq(Test::getId,2);
        lambdaUpdateWrapper2.set(Test::getName,"test");
        boolean update1 = this.update(lambdaUpdateWrapper2);
 
        return update;
    }
 
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean insertAndUpdate() {
 
        Test test = new Test();
        test.setName("2222");
        boolean save = this.save(test);
 
        boolean b = this.updateByIdOne();
 
        return save;
    }
}

执行结果

子父方法都有事务注解,事务生效

demo3

@Service("testService")
public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements TestService {
 
    @Override
    public boolean updateByIdOne() {
        LambdaUpdateWrapper<Test> lambdaUpdateWrapper = new UpdateWrapper<Test>().lambda();
        lambdaUpdateWrapper.eq(Test::getId,1);
        lambdaUpdateWrapper.set(Test::getName,"test");
        boolean update = this.update(lambdaUpdateWrapper);
 
        if(update){
            throw new RuntimeException("updateByIdOne");
        }
 
        return update;
    }
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean insertAndUpdate() {
 
        Test test = new Test();
        test.setName("2222");
        boolean save = this.save(test);
 
 
        boolean b = this.updateByIdOne();
        return b;
    }
}

insertAndUpdate插入成功后又回滚,update 更新成功也回滚,事务生效

demo4

@Service("testService")
public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements TestService {
 
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public boolean updateByIdOne() {
        LambdaUpdateWrapper<Test> lambdaUpdateWrapper = new UpdateWrapper<Test>().lambda();
        lambdaUpdateWrapper.eq(Test::getId,1);
        lambdaUpdateWrapper.set(Test::getName,"test");
        boolean update = this.update(lambdaUpdateWrapper);
 
        if(update){
            throw new RuntimeException("updateByIdOne");
        }
 
        LambdaUpdateWrapper<Test> lambdaUpdateWrapper2 = new UpdateWrapper<Test>().lambda();
        lambdaUpdateWrapper2.eq(Test::getId,2);
        lambdaUpdateWrapper2.set(Test::getName,"test");
        boolean update1 = this.update(lambdaUpdateWrapper2);
 
        return update;
    }
 
    @Override
    public boolean insertAndUpdate() {
        boolean b = this.updateByIdOne();
 
        return b;
    }
}

以上代码一跑,结果就很清楚了。

1、在同类中调用,二个方法都有加上事务注解,生效

2、同类中,子方法有事务注解,父类方法无事务注解,在controller层调用父类方法,子方法事务不生效

3、同类中,子方法无事务注解,父类方法有事务注解,在controller层调用父类方法,之方法事务生效

这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java实现简单的贪吃蛇小游戏

    Java实现简单的贪吃蛇小游戏

    这篇文章主要为大家详细介绍了Java实现简单的贪吃蛇小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • SpringBoot实现WebSocket服务并让客户端实时接收

    SpringBoot实现WebSocket服务并让客户端实时接收

    使用SpringBoot和WebSocket可创建实时消息推送服务,首先添加WebSocket依赖至pom.xml,配置WebSocket端点和逻辑处理器,通过WebSocketHandler处理消息,使用AnnouncementController模拟消息推送,支持HTML和微信小程序客户端接收消息,感兴趣的可以了解一下
    2024-10-10
  • Java预防SQL注入的具体实践方法

    Java预防SQL注入的具体实践方法

    在 Java 中预防 SQL 注入的核心是 避免直接拼接 SQL 语句,并通过参数化查询、ORM 框架和严格的输入验证来实现安全防护,以下是具体实践方法,感兴趣的小伙伴跟着小编一起来看看吧
    2025-01-01
  • java实现学生管理系统(面向对象)

    java实现学生管理系统(面向对象)

    这篇文章主要为大家详细介绍了java实现学生管理系统(面向对象),文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • Java实现Excel百万级数据导入功能的示例代码

    Java实现Excel百万级数据导入功能的示例代码

    这篇文章主要为大家详细介绍了Java如何实现Excel百万级数据导入功能,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考下
    2024-04-04
  • JavaSE、JavaEE和JavaWeb三大工程目录详解

    JavaSE、JavaEE和JavaWeb三大工程目录详解

    这篇文章主要给大家介绍了关于JavaSE、JavaEE和JavaWeb三大工程目录的相关资料,很多对java不是很了解的同学在看到课程⼤纲的时候发现⾥⾯出现了JavaSE、JavaEE、JavaME、JavaWEB这些词,搞得⼀头雾⽔,需要的朋友可以参考下
    2023-07-07
  • Java多线程编程中线程锁与读写锁的使用示例

    Java多线程编程中线程锁与读写锁的使用示例

    这篇文章主要介绍了Java多线程编程中线程锁与读写锁的使用示例,锁是控制程序多线程并发的重要手段,需要的朋友可以参考下
    2016-04-04
  • springcloud-gateway集成knife4j的示例详解

    springcloud-gateway集成knife4j的示例详解

    这篇文章主要介绍了springcloud-gateway集成knife4j的示例详解,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • java中关于转义字符的一个bug

    java中关于转义字符的一个bug

    本文主要介绍了java中关于转义字符的一个bug。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • SpringBoot深入浅出分析初始化器

    SpringBoot深入浅出分析初始化器

    这篇文章主要介绍了SpringBoot初始化器的分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07

最新评论