关于spring的各种异常捕获以及回滚失败问题

记录项目开发捕获异常以及回滚失败问题

开始

首先今天在写一个文件数据同步的接口时为了保证数据的严谨性,就想起了去设置事务手动回滚,由于之前一直没仔细测试过回滚 ,于是今天就开始了我的踩坑之路。话不多说上代码:

以下分为四种情况

一,设置TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();手动回滚(此时回滚失效)


    @PostMapping("/update")
    @ApiOperation("文件更新")
    public Outcome update(@RequestBody List<DocumentFile>fileList) {
        try{
            Long fileReportId = fileList.get(0).getZdswjbsId();
            DocumentSubEntity newReport = documentSubService.getById(fileReportId);
            //再次上传 修改文件报送表 版本号+1
            newReport.setVersion(newReport.getVersion()+1);
            UserInfo userInfo = sysUserBusiness.getUserInfo();
            newReport.setUpdateBy(userInfo.getId());
            newReport.setUploadTime(LocalDateTime.now());
            newReport.setUpdateTime(LocalDateTime.now());
            Boolean updateReport = documentSubService.updateById(newReport);
            //先根据文件报送表id删除文件表信息再重新添加文件信息
            Boolean removeFile = documentFileService.remove(new QueryWrapper<DocumentFile>().lambda().eq(ObjectUtil.isNotNull(fileReportId),DocumentFile::getZdswjbsId,fileReportId));
            List<DocumentFile> newFileList = fileList.stream().map(n-> n.setZdswjbsId(fileReportId)).collect(Collectors.toList());
            if(removeFile){
                documentFileService.saveBatch(newFileList);
            }else{
                throw new Exception("文件更新失败!");
            }
            log.info("文件更新同步开始");
            //先调人行端接口 根据uuid,version字段查询是否记录已同步,未同步就同步
            String uuid = newReport.getUuid();
            Long version = newReport.getVersion();
            List<DocumentSubEntity> docEntityList = docReportApiClient.selectListByUuidAndVersion(uuid,version);
            if(docEntityList.size()>0){
                log.info("uuid:"+uuid+",version:"+version+"的数据已同步" );
            }else{
                newReport.setFileList(newFileList);
                //调人行端接口进行上传
            }
            return Outcome.success("文件更新成功!");
        }catch (Exception e){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Outcome.failure("文件上传异常:"+e.getMessage());
        }
    }

最开始我设置了TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();手动事务回滚 ,但是我发现回滚失效,try catch里面对数据库的操作并没有被回滚!!!

二,加异常注解@Transactional(rollbackFor = Exception.class)(可以回滚)

后来我就想起了异常注解@Transactional(rollbackFor = Exception.class)如下:

    @Transactional(rollbackFor = Exception.class)
    @PostMapping("/update")
    @ApiOperation("文件更新")
    public Outcome update(@RequestBody List<DocumentFile>fileList) {
        try{
            Long fileReportId = fileList.get(0).getZdswjbsId();
            DocumentSubEntity newReport = documentSubService.getById(fileReportId);
            //再次上传 修改文件报送表 版本号+1
            newReport.setVersion(newReport.getVersion()+1);
            UserInfo userInfo = sysUserBusiness.getUserInfo();
            newReport.setUpdateBy(userInfo.getId());
            newReport.setUploadTime(LocalDateTime.now());
            newReport.setUpdateTime(LocalDateTime.now());
            Boolean updateReport = documentSubService.updateById(newReport);
            //先根据文件报送表id删除文件表信息再重新添加文件信息
            Boolean removeFile = documentFileService.remove(new QueryWrapper<DocumentFile>().lambda().eq(ObjectUtil.isNotNull(fileReportId),DocumentFile::getZdswjbsId,fileReportId));
            List<DocumentFile> newFileList = fileList.stream().map(n-> n.setZdswjbsId(fileReportId)).collect(Collectors.toList());
            if(removeFile){
                documentFileService.saveBatch(newFileList);
            }else{
                throw new Exception("文件更新失败!");
            }
            log.info("文件更新同步开始");
            //先调人行端接口 根据uuid,version字段查询是否记录已同步,未同步就同步
            String uuid = newReport.getUuid();
            Long version = newReport.getVersion();
            List<DocumentSubEntity> docEntityList = docReportApiClient.selectListByUuidAndVersion(uuid,version);
            if(docEntityList.size()>0){
                log.info("uuid:"+uuid+",version:"+version+"的数据已同步" );
            }else{
                newReport.setFileList(newFileList);
                //调人行端接口进行上传
            }
            return Outcome.success("文件更新成功!");
        }catch (Exception e){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Outcome.failure("文件上传异常:"+e.getMessage());
        }
    }

然后就发现这样是支持回滚的

三,去掉catch里面的手动回滚(回滚失效)

于是我就想如果不加手动回滚保留try catch捕获异常,加上异常注解会咋样

去掉catch里面的 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();代码

此时回滚失效

四,去掉try catch ,只加异常注解@Transactional(rollbackFor = Exception.class)(可以回滚)

这时,我把try catch里面捕获异常的代码去掉后 ,可以回滚,但是控制台会出现一些异常信息

openapi.sdk.common.exception.OpenApiClientException: 调用openapi异常:{"uuid":"90355cdb7ebf444da4c8322610233128","code":-1,"message":"??????opeapi???","dataType":"TEXT"}
	at openapi.client.sdk.OpenApiClient.doCall(OpenApiClient.java:486)
	at openapi.client.sdk.OpenApiClient.callOpenApi(OpenApiClient.java:196)
	at openapi.client.sdk.OpenApiClient.callOpenApi(OpenApiClient.java:227)
	at openapi.client.sdk.proxy.OpenApiRefProxyInvocationHandler.invoke(OpenApiRefProxyInvocationHandler.java:95)
	at com.sun.proxy.$Proxy155.selectListByUuidAndVersion(Unknown Source)
	at com.yc.boot.pboc.api.DocReportApi.update(DocReportApi.java:126)
	at com.yc.boot.pboc.api.DocReportApi$$FastClassBySpringCGLIB$$62919ac5.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
......
.....
....

总结

情况1:如果没有在程序中手动捕获异常 ----会回滚

@Transactional(rollbackFor = { Exception.class })  
public void test() throws Exception {  
     method1();  
     method2();//假如这个操作数据库的方法会抛出异常,现在方法method1()对数据库的操作   会回滚。  
}  

情况2:如果在程序中自己捕获了异常(使用了try catch) ------不会回滚

@Transactional(rollbackFor = { Exception.class })  
public void test() {  
     try {  
        method1();  
        method2();//假如这个操作数据库的方法会抛出异常,现在方法method1()对数据库的操作  不会回滚。  
     } catch (Exception e) {  
           e.printStackTrace();     
     }  
}  

情况3:现在如果我们需要手动捕获异常,并且也希望抛异常的时候能回滚肿么办呢?
下面这样写就好了,手动回滚事务:

@Transactional(rollbackFor = { Exception.class })  
public void test() {  
     try {  
        method1();  
        method2();  
     } catch (Exception e) {  
          e.printStackTrace();     
          TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//就是这一句了,加上之后,如果method2()抛了异常,                                                                                       //method1()是会回滚的  
     }  
}  

情况四:只加手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();-----此时并不会回滚


public void test() {  
     try {  
        method1();  
        method2();  
     } catch (Exception e) {  
          e.printStackTrace();     
          TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//就是这一句了,加上之后,如果method2()抛了异常,                                                                                       //method1()不会回滚
     }  
}  

写代码一定得注意数据严谨性!!!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

厌世小晨宇yu.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值