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

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

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

滕青山YYDS   2022-11-16 我要评论

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

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

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们