导读:本文通过一个财务接口例子来帮助大家理解事务传播行为概念和应用场景。
一、业务场景:财务模块需编写一个冻结接口,冻结操作细分为以下4个操作
- 创建冻结流水
- 修改资金表
- 创建冻结明细
- 修改流水状态
二、设计要求:
- 创建流水冻结作为一个独立事务,流水表示一个操作记录,当操作成功将流水状态设置为成功。
- 当修改资金表、创建明细、修改流水状态出现异常时回滚事务(不包含创建冻结流水)并将流水状态设置为失败,并返回包含错误提示的响应体。。
三、代码看点:
- accAmtFreezeOpt 冻结接口实现
- excRollbackAndUtpSeriAndPacResp 通用异常解决方法
- insertFreezeSerial 创建冻结流水接口实现
//Demo代码
/** * 冻结接口 * 1、创建冻结流水 * 2、修改账户资金 * 3、创建冻结明细 * 4、更新流水状态 */@Transactional(rollbackFor = Throwable.class)@Overridepublic CommonResponse accAmtFreezeOpt(AccAmtFreezeReq req, T interReqObj) {try { // 创建流水coreFreeze.insertFreezeSerial(obj);} catch (Exception e) { //手动事务回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return new CommonResponse<>(CommonCode.Create_Serial_Exception);}try { //修改资金表逻辑代码} catch (Exception e) { return excRollbackAndUtpSeriAndPacResp(coreSerialId, CommonCode.update_fund_main_error);}try { //创建明细逻辑代码} catch (Exception e) { return excRollbackAndUtpSeriAndPacResp(coreSerialId, CommonCode.Create_freeze_detail_error);}try { //更新流水逻辑代码} catch (Exception e) {return excRollbackAndUtpSeriAndPacResp(coreSerialId, CommonCode.Update_Serial_Exception);}//封装返回结果returen response}
//通用异常处理接口
private final CommonResponse excRollbackAndUtpSeriAndPacResp(String coreSerialId, CommonCode code) { // 手动回滚当前事务 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); try { // 更新流水状态为失败 } catch (Exception e) { log.error("更新冻结流水状态失败"); } return new CommonResponse<>(code);}
//创建冻结流水接口
@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic int insertFreezeSerial(FinanceFreezeSerialWithBLOBs serial) { //insert操作 return count;}
四、解析
a.传播行为枚举
- REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。
- NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED 。
b.代码解析
- 因为冻结操作作为一个大事务,所以在accAmtFreezeOpt冻结接口加上@Transactional注解,默认传播行为REQUIRED,当有事务则加入,没有则创建一个新事务。
- 根据要求创建流水操作需作为一个独立事务,但是代码中出现了事务的嵌套。解决方式:通过在创建流水接口加上@Transactional(propagation = Propagation.REQUIRES_NEW)注解。REQUIRES_NEW表示创建一个新的事务,如果当前存在事务,则把当前事务挂起。通过设定这种传播行为,可以保证创建流水操作独立为一个事务,并且不会在大事务中被回滚。
- 捕获异常后调用excRollbackAndUtpSeriAndPacResp通用异常处理方法,手动进行大事务回滚,并将流水状态更新为失败。
c.注意
- 方法A使用this调用同一个类中的方法B的时,方法B上的事务注解会失效。(在同一个类中的方法调用,不会被方法拦截器拦截到,因此事务不会起作用)。
- 事务需要方法抛出异常,其捕获异常后进行rollback操作,如果方法内部自己处理了异常则需要throw将异常抛出,否则会使直接失效。或通过手动进行回滚操作。
五、总结
通过设定传播行为propagation处理事务嵌套情况,编写完逻辑代码后结合业务分析对事务进行合理地控制。
感谢您的阅读,如果喜欢本文欢迎关注和转发,本头条号将持续分享IT技术知识。对于文章内容有其他想法或意见建议等,欢迎提出共同讨论共同进步。