【发布时间】:2014-11-12 02:45:58
【问题描述】:
我们将 Ticket 对象队列持久化到数据库中。我们包含队列的实体如下所示:
@Entity
public class StoreQueueCollection {
@Id
private int storeId;
@ManyToMany(fetch=FetchType.LAZY)
@OrderColumn
private List<Ticket> mainQueue = new ArrayList<Ticket>();
@ManyToMany(fetch=FetchType.LAZY)
@OrderColumn
private List<Ticket> cancelledQueue = new ArrayList<Ticket>();
.. etc
我们有一个操作将工单从一个队列移动到另一个队列(称为 changeStatus),另一个操作将新工单添加到队列末尾(称为 newTicket)。
当两个操作在同一个队列上交错时,这些操作基本上可以工作,但我们最终会在我们的队列中产生一个“间隙”。在数据库中,这看起来像是表的 order 列中缺少索引,像这样:0、1、2、4。当队列重新加载到 Java 中时,缺少的索引成为队列中的空元素。
我们在 StoreQueueCollection 对象上使用悲观锁定来防止不一致的交错更新,但它并没有像我们预期的那样工作。通过额外的日志记录,我们会看到如下奇怪的序列:
- changeTicketStatus() 开始 - 使用 entityManager.refresh() 锁定队列 - 票 X 从队列 A 前面移除 - 队列是 entityManager.flush()ed * newTicket() 开始 * 创建一个新票 Y,使用 entityManager.refresh() 锁定 StoreQueueCollection; * 票 Y 添加到队列 A 的末尾 * 票 Y 的字段已初始化并且是 save()d * 调用 refresh(),方法被阻塞 - changeTicketStatus() 恢复 (打印内存队列显示票 X 不在队列 A 中) - 票 X 添加到另一个队列 B - 票 X 修改了一些字段 - 票据 X 使用 repository.save() 保存 (打印内存队列显示票 X 不在队列 A 中) - changeTicketStatus() 完成 * newTicket() 恢复 * 刷新()返回 (打印内存队列 A 显示票 X 仍在队列中!) * 票 Y 被添加到队列 A 的末尾 (打印内存队列 A 显示票 X 和 Y 在队列中) * 队列是 save()d所有锁都是 LockModeType.PESSIMISTIC_WRITE,作用域是 PessimisticLockScope.EXTENDED。
在这一系列执行之后,另一个线程触发一个断言,该线程检查队列中的空条目。神秘的是,队列基本正确(X被删除,Y被添加到末尾),但是在Y之前的order列有一个间隙。
非常感谢您对我们做错的任何建议!
【问题讨论】:
-
您的锁定似乎有问题。您能否提供您的锁定和事务边界代码的简化示例?
-
感谢您的回复。不幸的是,它有点大,而且分布在不同的方法中。
-
在 SQL 级别记录(使用 log4jdbc-log4j2),看起来锁定没有按预期工作。看来我需要再次阅读文档。
标签: java hibernate jpa pessimistic-locking