记录项目开发捕获异常以及回滚失败问题
文章目录
开始
首先今天在写一个文件数据同步的接口时为了保证数据的严谨性,就想起了去设置事务手动回滚,由于之前一直没仔细测试过回滚 ,于是今天就开始了我的踩坑之路。话不多说上代码:
以下分为四种情况:
一,设置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()不会回滚
}
}
写代码一定得注意数据严谨性!!!