java异常捕捉陷阱(事务的异常,内存泄漏,finally块,catch块,继承得到的异常)
事务的异常回滚
默认spring只在发生未被捕获的runtimeexcetpion时才回滚。
1、事务不是遇到所有异常都会回滚,默认只有遇到遇到运行异常(RuntimeException)和程序错误(Error)才会回滚,非运行异常必须在
@Transactional 注解中使用 rollbackFor 属性来指定异常,比如:@Transactional(rollbackFor = Exception.class),才会回滚。
2、我们在处理异常时,有两种方式,要么抛出去,让上一层来捕获处理;要么把异常 try...catch 掉,在异常出现的地方给处理掉。就因为有这个 try...catch,所以导致异常被 “吃” 掉,事务无法回滚。遇到异常直接往上抛,给上一层来处理即可,千万不要在事务中把异常自己 ”吃“ 掉。
今天有个业务逻辑流程为:
1.访客预约确认先更新预约状态为“预约确认”
2.调用http接口发短信、更新预约状态为“预约成功”等一系列操作
这里面有个问题,如果第2步调接口异常或返回失败。则第1步需要回滚。开始我的代码如下:
public void updateStatusConfirm(int id) throws Exception { appointmentMapper.updateStatusConfirm(id); String res = Httper.get(appConfirmUrlPre + URL_SUFFIX + id); log.info("预约确认接口返回值为:" + res); if (StringUtils.isBlank(res) || !"0".equals(JSON.parseObject(res).getString("resultCode"))) { throw new Exception("预约确认接口返回失败,请稍后重试!"); } }
结果发现第1步执行成功,而第2步返回失败,事务并没有回滚。
后来才发现,捕获了异常不会发生事务回滚,除非抛出RuntimeException异常,更改代码如下:
public void updateStatusConfirm(int id) throws Exception { appointmentMapper.updateStatusConfirm(id); String res = Httper.get(appConfirmUrlPre + URL_SUFFIX + id); log.info("预约确认接口返回值为:" + res); if (StringUtils.isBlank(res) || !"0".equals(JSON.parseObject(res).getString("resultCode"))) { throw new RuntimeException("预约确认接口返回失败,请稍后重试!"); } }
就可以了。
这也意味着在service层最好不要捕获异常,否则不会回滚。
如捕获异常时,需要回滚需加上:代码级控制:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
try{ ... }catch(Exception e){ log.error(e.getMessage(),e); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); throw new RuntimeException( e.getMessage(), e ); }
https://blog.csdn.net/qingmengwuhen1/article/details/80969781

浙公网安备 33010602011771号