最近在做一个订单的钉钉审批功能,钉钉审批通过之后,订单更新审核状态,然后添加一条入库,并且更新入库状态:
// 订单审批通过@Transactional(rollbackFor = Exception.class) public void orderPass() { // 更新订单审核状态 updateOrderAuditStatus(id); // 添加入库 addPutInStorage(id); // 更新订单入库状态 updateOrderStorageStatus(id); }其中的添加入库是远程ERP入库,添加出库之后更新出库状态。因为ERP可能因为库存不足,会入库失败。但此时审批流程已经结束,不可能再发起一遍审批流程。当添加入库失败时订单审核状态正常更新,添加入库和更新入库状态失败。这里的解决方案是:
拆分成两个方法,一个是更新订单审核状态,另一个添加入库和更新入库状态。添加入库和更新入库状态开启一个事务,也就是添加嵌套事务 REQUIRES_NEW,REQUIRES_NEW表示无论是否有事务,都会创建一个新的事务。
修改后的代码如下:
// 订单审批通过@Transactional(rollbackFor = Exception.class)public void orderPass() { // 更新订单审核状态 updateOrderAuditStatus(id); try { // 更新出库 updatePutInStorage(id); } catch (Exception e) { System.out.println("更新出库失败"); }}// 更新出库@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)public void updatePutInStorage(Long id) throws Exception{ // 添加入库 addPutInStorage(id); // 更新订单入库状态 updateOrderStorageStatus(id); System.out.println("更新出库成功");}上面讲代码拆分成更新订单审核状态和更新入库,其中更新入库报错会被try catch异常捕获,不会影响到订单审核状态更新。而添加入库和更新订单入库状态处于同一个事务下,要么同时成功,要么同时失败。上述问题也解决了。
然而运行结果:
com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction锁超时了,为什么会有锁呢?主要是这里添加了REQUIRES_NEW。
死锁:两个线程为了保护两个不同的共享资源而使用了两个互斥锁,那么这两个互斥锁应用不当的时候,可能会造成两个线程都在等待对方释放锁,在没有外力的作用下,这些线程会一直相互等待,就没办法继续运行,这种情况就是发生了死锁。
上面锁超时原因,就是死锁的一种原因。所以需要把更新订单审核状态方法放在最后:
// 订单审批通过@Transactional(rollbackFor = Exception.class)public void orderPass() { try { // 更新出库 updatePutInStorage(id); } catch (Exception e) { System.out.println("更新出库失败"); } // 更新订单审核状态 updateOrderAuditStatus(id);}// 更新出库@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)public void updatePutInStorage(Long id) throws Exception{ // 添加入库 addPutInStorage(id); // 更新订单入库状态 updateOrderStorageStatus(id); System.out.println("更新出库成功");}业务上第一次遇到MySQL更新锁表超时( Lock wait timeout exceeded; try restarting transaction)
| 留言与评论(共有 0 条评论) “” |