Spring事务try catch后还发生了回滚

Spring事务的7种传播行为

1.REQUIRED(默认): 如果当前没有事务,就创建事务;如果当前存在事务,就加入该事务.
2.REQUIRES_NEW: 新建事务,如果当前存在事务,则把当前事务挂起,这个方法会独立提交事务,不受外部事务的影响,同时自身异常也不会影响外部事务.
3.SUPPORTS: 如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务方式运行.
4.NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起.
5.MANDATORY: 如果当前存在事务,就加入该事务;当前没有事务,则抛出异常,即外部方法必须有事务(学个单词:mandatory-强制性的;强制的;法定的;义务的).
6.NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常,即外部方法必须无事务.
7.NESTED: 如果当前存在事务,则它成为外部事务的一个内嵌事务,只有等外部事务提交才能提交;如果当前没有事务,则该取值等价于REQUIRED

记一种try catch之后还发生回滚的情况

	@Override
    @Transactional(rollbackFor = Exception.class)
    public void transactionA() {
        try {
            T t = new T();
            t.setK(11);
            this.baseMapper.insert(t);
            TransactionBService.transactionB();//TransactionBService通过Spring容器注入
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transactionB() {
        T t = new T();
        t.setK(22);
        this.baseMapper.insert(t);
        throw new RuntimeException("error");
    }

A方法和B方法,都标记了@Transactional(rollbackFor = Exception.class),在A方法中调用了B方法,B方法报错,A方法和B方法都进行了回滚,没有插入数据库.
原因分析: 首先我们要知道,外部经过Spring容器调用service的方法,事务才会生效,service类内部互相调用方法事务是不会生效的,Spring事务的实现基础是AOP, 而AOP的原理是动态代理,若是类自身的调用,而不是代理对象去调用,就不会产生AOP,这样Spring就不能把你的代码织入到约定的流程中去.
接着讲上面这个问题,A方法调用B方法,会在B方法的时候启动代理,然后再去执行B的方法.代理发现当前存在事务,且B方法的传播行为为Propagation.REQUIRED,方法的传播行为为Propagation.REQUIRED,就会把B的事务并入到当前事务中.当B方法抛出异常,就已经把A方法的事务(当前事务)标记为回滚,然后抛出异常;这个时候A方法try catch捕获异常,但事务已经是回滚状态了.

@Transactional(rollbackFor = Exception.class)

Exception分为运行时异常RuntimeException和非运行时异常.当@Transactional注解作用于类上时,该类的所有public方法都将具有将传播类型的事务,我们也可以把该注解在方法上,这样不同方法就可以指定各自的事务传播类型.
@Transactional 注解中如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事务在遇到非运行时异常时也回滚.

额外基础小知识:
运行时异常是指RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理.这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生.
非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类.如IOException、SQLException等以及用户自定义的Exception异常.对于这种异常,JAVA编译器强制要求我们必需对出现的这些异常进行catch并处理,否则程序就不能编译通过.

### Spring 框架中 Try-Catch事务回滚的影响 在Spring框架内,`try-catch`结构确实能够影响到由`@Transactional`注解所定义的方法中的事务回滚行为。具体来说: - 默认情况下,只有未被捕捉的运行时异常(即继承自`RuntimeException`的异常)才会触发事务自动回滚[^2]。 - 如果在一个带有`@Transactional`注解的方法里使用了`try-catch`来捕获异常,则取决于如何处理这个异常以及是否有额外设置。 对于已检查异常,默认情况下它们不会引起事务回滚;但是可以通过指定`rollbackFor`参数让某些类型的已检查异常也导致回滚操作。例如,在代码片段中可以看到如下配置: ```java @Transactional(rollbackFor = Exception.class) public void asyncJob() { success(); try { exception(); } catch (Exception e) { e.printStackTrace(); // 手工回滚异常 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } ``` 这段代码展示了如何利用`TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()`来进行手动回滚的操作[^3]。这使得即便是在`catch`块中处理过的异常也能使当前事务标记为只读模式从而实现回滚效果。 另外需要注意的是,如果希望整个方法执行失败后都能正常回滚而不论是否进入了`catch`分支,那么应该避免完全吞掉所有可能发生的错误而不做任何响应或记录。比如下面的例子就可能导致预期之外的行为——尽管设置了全局性的回滚条件(`rollbackFor=Exception.class`),但由于存在空返回值且没有任何日志输出或其他形式的通知机制,可能会掩盖实际发生的问题[^5]: ```java @Override @Transactional(rollbackFor = Exception.class) public CommonResponse modifyTeaFormula(GemiTeaFormulaReq gemiTeaFormulaReq) { try { /* 省略部分业务逻辑 */ teaFormulaDao.deleteById(id); teaFormulaDetailDao.deleteById(id); } catch (Exception e) { logger.error("error:" + e.getMessage()); } return new CommonResponse(0); } ``` 为了确保事务按期望工作,建议遵循最佳实践:要么允许异常传播出去以便于框架可以识别并且回滚相应的更改;要么就在适当的位置加入显式的回滚指令,并考虑增加足够的调试信息帮助后续排查问题所在。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值