事务回滚

     转自:https://blog.csdn.net/ProGram_BlackCat/article/details/88230287

spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行commit or rollback(spring默认取决于是否抛出runtime异常).如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。一般不需要在业务方法中catch异常,如果非要catch,在做完你想做的工作后(比如关闭文件等)一定要抛出runtime exception,否则spring会将你的操作commit,这样就会产生脏数据.所以你的catch代码是画蛇添足。

       在项目中 @service层中 我们会经常在做一些增删改操作的方法上看到 spring 的事务注解 @transaction。@transaction 是让spring 帮我们实现事务的控制。但是在项目中会经常看到 有的方法中 会存在trycatch块包括的方法上注解着@transaction

eg:

 @Override
    @Transactional
    public Json addOrder(TOrderAddReq tOrderAddReq) {
        try{
                //增删改方法
        } catch (Exception e) {
            .....
            e.printStackTrace();}
//        }
        return json;
    }


上述的方法执行后可以看到事务并没有执行,接下来再看一个例子eg:

 @Override
    @Transactional
    public Json addOrder(TOrderAddReq tOrderAddReq) {
        try{
                //增删改方法
        } catch (Exception e) {
            // 手动硬编码开启spring事务管理
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            e.printStackTrace();}
//        }
        return json;
    }


上述方法执行后我们可以看到事务最后执行了,但实际上 事务 执行只是因为手动硬编码开启spring事务管理起了作用 而方法上的注解并没有起作用,接下来再看一个例子eg:

 

@Override
    @Transactional
    public Json addOrder(TOrderAddReq tOrderAddReq) {
        try{
                //增删改方法
        } catch (Exception e) {
            
            throw new RuntimeException();
            }
//        }
        return json;
    }


上述方法执行后我们可以看到事务是执行了的,但这里有个小细节:@Transactional不做任何配置 默认是对抛出的unchecked异常回滚,checked异常不会回滚,为了让所有异常都会让事务启动可以将 @Transactional配置为 @Transactional(rollbackFor = Exception.class)

注解事务@Transactional

 

@Service
public class PersonService {
    @Resource
    private PersonMapper personMapper;

    @Resource
    private CompanyMapper companyMapper;

    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public void saveOne(Person person) {
        Company company = new Company();
        company.setName("tenmao:" + person.getName());
        companyMapper.insertOne(company);
        personMapper.insertOne(person);
    }
}

注解属性

  • rollbackFor触发回滚的异常,默认是RuntimeExceptionError
  • isolation: 事务的隔离级别,默认是Isolation.DEFAULT也就是数据库自身的默认隔离级别,比如MySQL是ISOLATION_REPEATABLE_READ可重复读

这样就可以了,不需要其他配置。

ps:网络上还说要在@SpringBootApplication上添加注解@EnableTransactionManagement,在springboot2+已经不需要了



 

spring事务——try{...}catch{...}中事务不回滚的几种处理方式
https://www.cnblogs.com/zjfjava/p/9963958.html

当希望在某个方法中添加事务时,我们常常在方法头上添加@Transactional注解

@ResponseBody
    @RequestMapping(value = "/payment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    @Transactional
    public Payment paymentJson(@RequestBody PaymentRequestInfo entity) {
    //method
  }

容易让人忽略的是:方法上未加任何属性的@Transactional注解只能在抛出RuntimeException或者Error时才会触发事务的回滚,常见的非RuntimeException是不会触发事务的回滚的。

如果要在抛出 非RuntimeException时也触发回滚机制,需要我们在注解上添加 rollbackFor = { Exception.class }属性。

@ResponseBody
    @RequestMapping(value = "/payment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    @Transactional(rollbackFor = { Exception.class })
    public Payment paymentJson(@RequestBody PaymentRequestInfo entity) {
     //method
    }

当然,上面事务回滚的前提是添加@Transactional注解的方法中不含有try{...}catch{...}捕获异常,使得程序运行过程中出现异常能顺利抛出,从而触发事务回滚。

在实际开发中,我们往往需要在方法中进行异常的捕获,从而对异常进行判断,为客户端返回提示信息。但是此时由于异常的被捕获,导致事务的回滚没有被触发,导致事务的失败。

下面提供几种解决方法:

1. 使用@Transactional注解,抛出@Transactional注解默认识别的RuntimeException

方法上使用@Transactional注解,在捕获到异常时在catch语句中抛出RuntimeException。

2. 使用@Transactional(rollbackFor = { Exception.class }),抛出捕获的非RuntimeException异常

方法上使用@Transactional(rollbackFor = { Exception.class })注解声明事务回滚级别,在捕获到异常时在catch语句中直接抛出所捕获的异常。

3. 手动回滚

上面两个在catch{...}中抛出异常的方法都有个不足之处,就是不能在catch{...}中存在return子句,所以设置手动回滚,当捕获到异常时,手动回滚,同时返回前台提示信息。

坑点

常见坑点1:遇到检测异常时,事务默认不回滚。


例如下面这段代码,账户余额依旧增加成功,并没有因为后面遇到SQLException(检测异常)而进行事务回滚!!

 

 @Transactional
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //然后遇到故障
        throw new SQLException("发生异常了..");
    }


原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException及其子类)和程序错误(Error)才会进行事务回滚,显然SQLException并不属于这个范围。如果想针对检测异常进行事务回滚,可以在@Transactional 注解里使用
rollbackFor 属性明确指定异常。例如下面这样,就可以正常回滚:

@Transactional(rollbackFor = Exception.class)
public void addMoney() throws Exception {
    //先增加余额
    accountMapper.addMoney();
    //然后遇到故障
    throw new SQLException("发生异常了..");
}


常见坑点2: 在业务层捕捉异常后,发现事务不生效。


这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。例如:下面这段代码直接导致增加余额的事务回滚没有生效。

@Transactional
public void addMoney() throws Exception {
    //先增加余额
    accountMapper.addMoney();
    //谨慎:尽量不要在业务层捕捉异常并处理
    try {
        throw new SQLException("发生异常了..");
    } catch (Exception e) {
        e.printStackTrace();
    }
}


不要小瞧了这些细节,往前暴露异常很大程度上很能够帮我们快速定位问题,而不是经常在项目上线后出现问题,却无法刨根知道哪里报错。

推荐做法:在业务层统一抛出异常,然后在控制层统一处理。

@Transactional
public void addMoney() throws Exception {
    //先增加余额
    accountMapper.addMoney();
    //推荐:在业务层将异常抛出
    throw new RuntimeException("发生异常了..");
}

其他链接:

https://blog.csdn.net/cowbin2012/article/details/90751044

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值