我使用Hibernate和Joined-SubClasses将类层次结构映射到 数据库。不幸的是,当一个对象被更新而另一个线程试图加载相同的对象时,这会导致死锁。使用将 映射到单个表的对象,这不成问题。这似乎是MSSQL 在类层次结构的表上获取锁的方式造成的。Hibernate与MSSQL加入子类死锁
当Hibernate从数据库加载一个对象时,它使用SELECT与JOIN:
SELECT ...
FROM
subclass
LEFT JOIN class
ON ...
WHERE ...
当Hibernate更新该子类它的一个目的:
UPDATE
class
SET ...
WHERE ...
UPDATE
subclass
SET ...
WHERE ...
的问题是,如果一个对象在两个更新语句之间加载,它会导致死锁。 SELECT语句似乎锁定了另一个 之后的两个表。那么,什么似乎发生的是:
- 线程1个负载的目标和地方共享的两个表
- 线程1执行在类表中的UPDATE语句锁和升级的类表 锁独占锁。
- 线程2试图通过执行SELECT语句加载同一个对象,它 地方上的子类表的共享锁,然后等待直到在类表中的排他 锁被释放
- 线程1执行UPDATE语句对于子类表,它希望 将其在子类表上的锁升级为排它锁,但 表已被线程2锁定,该线程正在等待线程1
- 线程2由于死锁而中止线1
死锁图看起来像这样:Deadlock Graph
这些对象经常更新安静,并且这会导致始终造成死锁,甚至在加载单个对象时甚至会导致 。我也尝试用 HSQLDB重现问题,但是它不会死锁,HSQLDB似乎要么将两个表都锁定为 一次,要么等待它可以同时锁定两个表,所以这似乎是一个问题,只有 发生在MSSQL中。
如果不修改 模式(索引除外),用Hibernate避免这个问题的解决方案是什么?
我将Hibernate放在一边问你的问题,这实际上是一个比Hibernate问题更多的MSSQL问题(如果适当标记它,你可能会获得更多的牵引力)。无论如何你正在使用什么事务隔离? PS:甚至不尝试重现HSQLDB的问题(不确定使用的是哪个版本,但HSQLDB 1.8.2仅支持READ_UNCOMMITTED隔离级别,因此情况有很大不同)。 – 2010-09-02 15:02:24
也许,但加载一个对象的事实在两个表上创建了一个连接,并且对象的更新创建了2个独立的UPDATE语句来自Hibernate。如果我离开Hibernate,那么解决方案可能是在单独的事务中运行这两个更新,或者更改SELECT语句。但这是我不能做的事情,因为这些语句是由Hibernate生成的。 我首先使用HSQLDB 1.8.x进行了测试,注意到它不起作用,然后切换到2.0,这延迟了线程2的SELECT语句,直到线程1的事务被提交为止。 – Reboot 2010-09-02 15:45:55
陈述的产生与否与事实无关,这是我的观点。只要提出一个关于**这个**特定场景的问题(顺便说一句,在单独的查询中运行更新不会)。但随时忽略我的建议:) – 2010-09-02 19:50:17