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

目录
  • 总结几种@Transactional失效原因
    • 非public方法
    • 自调用问题
    • 异常相关问题
    • 抛出非运行时异常
    • 传播机制配置错误
  • @Transactional事务失效场景类内部调用实测
    • demo1
    • demo2
    • demo3
    • demo4

总结几种@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层调用父类方法,之方法事务生效

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

(0)

相关推荐

  • 探索Java中private方法添加@Transactional事务未生效原因

    现在产品期望用户创建和保存逻辑分离:把User实例的创建和保存逻辑拆到两个方法分别进行.然后,把事务的注解 @Transactional 加在保存数据库的方法上. @Service public class StudentService { @Autowired private StudentMapper studentMapper; @Autowired private StudentService studentService; public void saveStudent(String

  • java使用@Transactional时常犯的N种错误

    目录 1.在同一个类中调用 2. @Transactional修饰方法不是public 3. 不同的数据源 4. 回滚异常配置不正确 5. 数据库引擎不支持事务 小结 @Transactional是我们在用Spring时候几乎逃不掉的一个注解,该注解主要用来声明事务.它的实现原理是通过Spring AOP在注解修饰方法的前后织入事务管理的实现语句,所以开发者只需要通过一个注解就能代替一系列繁琐的事务开始.事务关闭等重复性的编码任务. 编码方式确实简单了,但也因为隐藏了直观的实现逻辑,一些错误的编

  • Spring事务注解@Transactional失效的八种场景分析

    首先说一下最近自己遇到的一个坑: @Transactional service A(){ try{ insert(); serviceB.update(); }catch(){ throw new RunTimeException(); } } serviceB(){ @Transactional update(){ try{ mapperB.update(); }catch(){ throw new RunTimeException(); } } } mapperB (){ try{ //do

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

    目录 总结几种@Transactional失效原因 非public方法 自调用问题 异常相关问题 抛出非运行时异常 传播机制配置错误 @Transactional事务失效场景类内部调用实测 demo1 demo2 demo3 demo4 总结几种@Transactional失效原因 非public方法 spring事务是通过动态代理的方法来实现的,有两种实现动态代理的方式,jdk动态代理方式是将目标对象放入代理对象内部,通过代理对象来访问目标对象:cglib字节码生成是通过生成目标对象的子类,通

  • spring中12种@Transactional的失效场景(小结)

    目录 一.失效场景集一:代理不生效 二.失效场景集二:框架或底层不支持的功能 三.失效场景集三:错误使用@Transactional 四.总结 数据库事务是后端开发中不可缺少的一块知识点.Spring为了更好的支撑我们进行数据库操作,在框架中支持了两种事务管理的方式: 编程式事务声明式事务 日常我们进行业务开发时,基本上使用的都是声明式事务,即为使用@Transactional注解的方式. 常规使用时,Spring能帮我们很好的实现数据库的ACID (这里需要注意哦,Spring只是进行了编程上

  • SpringBoot使用Async注解失效原因分析及解决(spring异步回调)

    目录 Async注解失效原因分析及解决(spring异步回调) Spring中@Async 有时候在使用的过程中@Async注解会失效 解决方式一 解决方式二 springboot @Async 失效可能原因 Async注解失效原因分析及解决(spring异步回调) Spring中@Async 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3.x之后,就已

  • 分析Springboot中嵌套事务失效原因详解

    首先两个事务方法,其中一个调用另一个. @Transactional(rollbackFor = Exception.class) public void trance() { try { trance1();//调用下一个事务方法. } catch (Exception e) { e.printStackTrace(); } User user = new User(); ShardingIDConfig shardingIDConfig = new ShardingIDConfig(); u

  • Spring MVC 注解自动扫描失效原因分析

    关于spring自动扫描,在控制层,采用注解配置@Controller,项目能够成功启动,且无任何报错.但是 在进行页面跳转时,并未进行相应的拦截,整个界面只能在默认界面 ,跳转报404,由于楼主初次尝试,在绕了一个大圈后,初步确认是在扫描时mvc控制器,并未成功,详情请看代码 <!-- 开启controller注解支持 --> <context:component-scan base-package="com.cjw.test.controller" use-def

  • PHP函数__autoload失效原因及解决方法

    1.利用sprintf()函数来格式化,语法"sprintf("%.小数位数f",$num)". <?php header("Content-type:text/html;charset=utf-8"); $num = 10.4567; $format_num = sprintf("%.2f",$num); echo $format_num; //10.46 ?> 2.利用number_format()函数,语法&

  • Nginx 502 bad gateway错误解决的九种方案及原因

    目录 前言 502系列错误代码 5XX系列错误代码 Nginx 502 Bad Gateway 错误的原因及解决方法 1.检查PHP基础设置 2.FastCGI进程是否已经启动 3.FastCGI worker进程数是否不够 4.FastCGI执行时间过长 5.FastCGI Buffer不够 6.FastCGI 缓冲区设置过小 7.代理缓冲区设置过小(使用了nginx反向代理的情况) 8.默认php-cgi的进程数设置过少 9.其它原因 总结 前言 502 Bad Gateway 是一种HTT

  • Java多线程事务回滚@Transactional失效处理方案

    目录 背景介绍 公用的类和方法 示例事务不成功操作 使用sqlSession控制手动提交事务 背景介绍 1,最近有一个大数据量插入的操作入库的业务场景,需要先做一些其他修改操作,然后在执行插入操作,由于插入数据可能会很多,用到多线程去拆分数据并行处理来提高响应时间,如果有一个线程执行失败,则全部回滚. 2,在spring中可以使用@Transactional注解去控制事务,使出现异常时会进行回滚,在多线程中,这个注解则不会生效,如果主线程需要先执行一些修改数据库的操作,当子线程在进行处理出现异常

  • MySQL索引失效原因以及SQL查询语句不走索引原因详解

    目录 前言 1. 隐式的类型转换,索引失效 2. 查询条件包含 or,可能导致索引失效 3. like 通配符可能导致索引失效 4. 查询条件不满足联合索引的最左匹配原则 5. 在索引列login_time上使用 mysql 的内置函数 6. 对索引列age进行列运算(如,+.-.*./), 索引不生效 7. 索引字段age上使用(!= 或者 < >, not in),索引可能失效 8. 索引字段上使用 is null, is not null,索引可能失效 (查询结果行数) 9. 左右joi

随机推荐