当前系统使用Spring声明式事务来做事务管理,出现了一个非常诡异的bug,且看代码辅助细说。
Spring声明式事务配置:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="handle*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="inner*" propagation="REQUIRES_NEW" rollback-for="Exception"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="interceptorPointCuts" expression="execution(* service..*.*(..))" /> <!-- 扫描路径service包下 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts"/>
</aop:config>
Service包下的接口:
public interfaceISPOrderService {
public enum DealStatusEnum {
RISK_FAIL_BEYOND_AMT("0", "风控审核失败,超出借款限额!"), RISK_FAIL_REJECT("1", "风控审核驳回!"), RISK_FAIL_EXCEPTION("2", "风控审核异常!"),
LACK_OF_CFG("3", "缺少配置信息!");
private final String code;
private final String name;
private DealStatusEnum(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
}
/**
* 处理订单
* @return
*/
public DealStatusEnum handleYlOrderReq(Stringreq_sn,String trans_date,String terminal_id,String merchant_id,String submit_time, String order_sn,String trans_amt, String acct_id);
/**
* 保存订单信息
* @return
*/
public boolean innerSaveOrder(Orderorder);
}
//实现类
@Service
public classSPOrderServiceImpl implements ISPOrderService {
privateLogger logger= LoggerFactory.getLogger(SPOrderServiceImpl.class);
@Autowired
privateOrderDao orderDao;
@Autowired
privateISPInnerOrderService innerOrderService;
@Autowired
privateRiskAuthService riskAuthService;
@Autowired
privateUserCardMapDao cardMapDao;
@Autowired
privateLoanTempCfgDao loanTempCfgDao;
@Autowired
privateLoanInfoDao loanInfoDao;
@Override
publicDealStatusEnum handleYlOrderReq(String req_sn, String trans_date, String terminal_id, String merchant_id,
String submit_time, String order_sn, String trans_amt, String acct_id) {
try {
BigDecimal transAmount = new BigDecimal("trans_amt");
Date currentTime = new Date();
Order order = new Order();
order.setCardNo(acct_id);
order.setCreateTime(new Date());
order.setLiquidationTime(DateUtil.getFormattedDate(DateUtil.DATE_FORMAT_YYYYMMDD,trans_date));
order.setMerchantNo(merchant_id);
order.setOrderNo(order_sn);
order.setOrderStatus(DictConstant.orderStatus.WATI_PAY.getCode());
order.setReturnAmount(BigDecimal.ZERO);
order.setSubmitTime(DateUtil.getFormattedDate(DateUtil.DATE_FORMAT_YYYYMMDDHHMMSS, submit_time));
order.setTransAmount(transAmount);
order.setTransTime(DateUtil.getFormattedDate(DateUtil.DATE_FORMAT_YYYYMMDDHHMMSS, submit_time));
innerSaveOrder(order);//新开事务,插入订单信息,风控审核系统要基于订单信息做风控审核
//风控审核
Map<String,Object> cardMapParam = newHashMap<String,Object>();
cardMapParam.put("cardNo", acct_id);
UserCardMapVO cardMapVo = cardMapDao.selectByParams(cardMapParam);
//调用风控系统,审核是否允许下单(另一个系统,post请求)
RiskResult authResult = riskAuthService.auditLoanClaim(String.valueOf(cardMapVo.getCustId()), order_sn);
if(! authResult.isSuccess()){
return DealStatusEnum.RISK_FAIL_EXCEPTION;
}
Order updateOrder = new Order();
updateOrder.setOrderNo(order_sn);
updateOrder.setOrderStatus(DictConstant.orderStatus.PAIED.getCode());
orderDao.updateByPrimaryKeySelective(updateOrder);
//TODO 后续逻辑,此处省略
} catch (Exception e) {
logger.error("handleYlOrderReq exception 4 param,req_sn:"+req_sn+" trans_date:"+trans_date+" merchant_id:"+merchant_id
+" submit_time:"+submit_time+" order_sn:"+order_sn+" trans_amt:"+trans_amt+" acct_id:"+acct_id,e);
}
return null;
}
@Override
public boolean innerSaveOrder(Orderorder){
orderDao.insert(order);
return true;
}
}
在以上处理订单的逻辑中,我在方法handleYlOrderReq中调用了同一个service中的方法innerSaveOrder,在事务配置中,我给inner配置的是ropagation="REQUIRES_NEW" ,也就是新开事务处理,因此我期望的效果是在执行到innerSaveOrder(order);这行代码,新开事务,将订单信息保存到数据库,后续调用风控系统,风控系统根据保存的订单信息来做风控审核。但事与愿违,执行到innerSaveOrder(order);并没有提交事务,而是要等到方法handleYlOrderReq执行结束才会提交事务插入订单信息,因此执行到以下代码
RiskResult authResult = riskAuthService.auditLoanClaim(String.valueOf(cardMapVo.getCustId()), order_sn);
请求风控系统的时候,其实库里还没有当前这笔订单,一次风控审核始终失败。经过查证,发现导致该问题的原因。方法handleYlOrderReq与方法innerSaveOrder都是在同一个service中的,Spring对事物管理的原则是,同一个Service中的方法嵌套调用,事务类型以入口方法(此处为handleYlOrderReq)为准,被调用的方法继承调用它的事务类型,也就是配置的<tx:method name="handle*" propagation="REQUIRED" rollback-for="Exception"/>,因此两个方法是同一个事务,在方法handleYlOrderReq执行结束之前,方法innerSaveOrder的插入语句不会作为一个新开事务提交。
综上,如果希望新开事务,那么一定要在另外一个service中去实现这个方法。