问题背景
在做app作业一的第二部分时,遇到了问题(自以为是问题,没想到老师是故意让我们发现这是个bug)。
flowchart LR; A(OrderController::createOrder) --> B(OrderService::createOrder); B --> C(OrderDao::addOne); B --> D(OrderItemDao::addList);
如图,在电子书服务系统E-Book
中,我们进行订单创建,一个订单order
中包含多个订单项orderItem
。
现在我们要对OrderService::createOrder
,OrderDao::saveOne
和OrderItemDao::saveList
进行事务传播控制,分别简称三部分为A,B,C。
若A和B设置为REQUIRED
,而C设置为REQUIRES_NEW
,则会出现死锁问题。
源代码大致如下。
1 | // OrderServiceImpl.java |
执行结果为死锁。
原因分析
在B执行后,由于与A处在同一事务一里,则事务一拿到了order
表中新插入行的锁,不管事务隔离属性如何设置,其他事务均不可进行写操作。而在随后的C流程中,写入orderItems
的时候,order_item
表中有order
的外键,于是他在给order_item
新插入的数据上锁之外,也要在order
表中对外键所指向的数据加锁。
然而C是拿不到的,因为B已经拿到了。
于是此时,C等待B放锁,但是同时,B又等待C结束返回,从而形成了死锁。
解决方法
该问题说明,并不是所有业务使用REQUIRES_NEW
传播都可以,在此场景下,都使用默认的REQUIRED
传播属性即可。