目录
- 总结几种@Transactional失效原因
- 非public方法
- 自调用问题
- 异常相关问题
- 抛出非运行时异常
- 传播机制配置错误
- @Transactional事务失效场景类内部调用实测
- demo1
- demo2
- demo3
- demo4
总结几种@Transactional失效原因
非public方法
sprinhttp://www.devze.comg事务是通过动态代理的方法来实现的,有两种实现动态代理的方式,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: 以非事务方式运行,如android果当前存在事务,则抛出异常。
@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);
lambd编程客栈aUpdateWrapper.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,开发者_JAVA开发 Test> implements TestService {
@Transactional(rollbackFor = RuntimeException.class)
@Override
public boolean updateByIdOne() {
Lamhttp://www.devze.combdaUpdateWrapper<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 insertAndUpdatejavascript() {
boolean b = this.updateByIdOne();
return b;
}
}

以上代码一跑,结果就很清楚了。
1、在同类中调用,二个方法都有加上事务注解,生效
2、同类中,子方法有事务注解,父类方法无事务注解,在controller层调用父类方法,子方法事务不生效
3、同类中,子方法无事务注解,父类方法有事务注解,在controller层调用父类方法,之方法事务生效
这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持我们。
加载中,请稍侯......
精彩评论