Java Hibernate使用MSSQL连接子类死锁

Java Hibernate使用MSSQL连接子类死锁,java,sql-server,hibernate,deadlock,Java,Sql Server,Hibernate,Deadlock,我使用Hibernate和连接的子类将类层次结构映射到 数据库不幸的是,当对象更新时,这会导致死锁 不同的线程正在尝试加载相同的对象。使用已映射的对象 对于一张桌子来说,这没有问题。这似乎是由MSSQL的方式引起的 获取类层次结构表上的锁 当Hibernate从数据库加载对象时,它会使用带有联接的SELECT: SELECT ... FROM subclass LEFT JOIN class ON ... WHERE ... 当Hibernate更新此子类的对象

我使用Hibernate和连接的子类将类层次结构映射到 数据库不幸的是,当对象更新时,这会导致死锁 不同的线程正在尝试加载相同的对象。使用已映射的对象 对于一张桌子来说,这没有问题。这似乎是由MSSQL的方式引起的 获取类层次结构表上的锁

当Hibernate从数据库加载对象时,它会使用带有联接的SELECT:

SELECT ...
FROM
    subclass
    LEFT JOIN class
        ON ...
WHERE ...
当Hibernate更新此子类的对象时,它会:

UPDATE
    class
SET ...
WHERE ...

UPDATE
    subclass
SET ...
WHERE ...
问题是,如果在两个update语句之间加载了对象,它将 导致死锁。SELECT语句似乎一个接一个地锁定了这两个表 另一个因此,似乎发生的是:

  • 线程1加载一个对象并在两个表上放置共享锁
  • 线程1执行类表的UPDATE语句并升级 将类表上的锁锁定为独占锁
  • 线程2试图通过执行SELECT语句来加载相同的对象,它 在子类表上放置共享锁,然后等待直到 类表上的锁被释放
  • 线程1执行子类表的UPDATE语句,它希望 将子类表上的锁升级为独占锁,但 表已被线程2锁定,该线程正在等待线程1
  • 由于与线程1的死锁,线程2被中止
  • 死锁图如下所示:

    这些对象经常被安静地更新,这会一直导致死锁,甚至 仅加载单个对象时。我还试图用计算机重现这个问题 HSQLDB但它不会死锁,HSQLDB似乎会同时锁定这两个表 一次或等待,直到它可以锁定两者,所以这似乎是一个问题,只有 与MSSQL一起发生

    如果不修改Hibernate,有什么解决方案可以避免这个问题
    模式(索引除外)

    在我看来,这些更新需要在单个事务中以原子方式完成。不幸的是,我没有太多关于Hibernate的背景知识,所以我将把它留给其他人,让他们为您指出正确的方向。

    您打开了吗?这将有助于准确识别导致死锁的资源。有关更多信息,请参阅上的MSDN文章


    这些表上有索引吗?如果是这样,如果应用程序在聚集索引上获取锁,然后通过查找非聚集索引尝试在同一个表上获取更多锁,则可能会发生死锁。

    它们在单个事务中执行。如果不是,则不会导致与其他事务的死锁。两个线程的活动在一个事务中完成。也许我应该说得更清楚。我会把Hibernate放在一边问你的问题,这实际上是一个MSSQL问题,而不是Hibernate问题(如果你给它加上适当的标签,你可能会得到更多的关注)。不管怎样,您使用的是什么事务隔离?PS:甚至不要试图重现HSQLDB的问题(不确定您使用的是什么版本,但HSQLDB1.8.2只支持读取未提交隔离级别,因此情况非常不同),但是加载一个对象会在两个表上创建一个连接,并且一个对象的更新会创建两个独立的update语句,这一事实来自Hibernate。如果我不使用Hibernate,那么解决方案可能是在单独的事务中运行这两个更新,或者更改SELECT语句。但这是我不能做的,因为这些语句是由Hibernate生成的。我首先使用HSQLDB1.8.x进行了测试,发现它不起作用,然后切换到2.0,这将延迟线程2的SELECT语句,直到提交线程1的事务。生成或不生成语句这一事实与此无关,这就是我的观点。只需问一个关于这个特定场景的问题(顺便说一句,这是正确的,在单独的查询中运行更新是不正确的)。但请不要理会我的建议:)我设法在我的selects中使用了LockMock.Upgrade,但这只适用于基类表,而不适用于子类表。因此,当NHibernate最终尝试实际更新子类时,仍然会出现死锁。下面是我在nhusers上的帖子,详细介绍了我的发现:我用SQL Management Studio Profiler创建了一个死锁图,并在问题中添加了死锁图。在这两个表上,只访问一行,对我来说,select语句似乎在以与UPDATE语句相反的顺序锁定这两个表。每个表中只访问一行。我不知道索引如何帮助改变这一点。