Hibernate如何决定更新/插入/删除的顺序

时间:2012-09-27 07:35:26

标签: hibernate deadlock database-deadlocks

让我们先忘掉Hibernate。假设我有两张桌子,A& B.两个事务正在更新这两个表中的相同记录,但是txn 1更新B然后更新A,而txn 2更新A然后更新B.这是典型的死锁示例。避免这种情况的最常见方法是预先定义获取资源的顺序。例如,我们应该更新表A,然后更新B.

回到Hibernate。当我们在一个会话中更新大量实体时,一旦我刷新会话,不同实体的更改将为DB生成相应的插入/更新/删除语句。 Hibernate是否有一些算法来决定实体之间的更新顺序?如果没有,Hibernate用于防止第1段中描述的死锁情况的方式是什么?

如果Hibernate维持订单,我如何知道或控制订单?我不希望我的数据库中的显式更新与Hibernate冲突,并导致死锁。

2 个答案:

答案 0 :(得分:35)

您描述的问题不是由数据库处理的,根据我的经验,Hibernate也没有完全处理。

你必须采取明确的措施来避免这是一个问题。

Hibernate为您完成了一些工作。根据之前的回答,Hibernate确保在隔离的刷新内对插入,删除和更新进行排序,以确保它们以可实现的顺序应用。请参阅AbstractFlushingEventListener类中的performExecutions(EventSource session)

  

以特殊顺序执行所有SQL(和二级缓存更新),以便不会违反外键约束:

     
      
  1. 按照执行顺序插入
  2.   
  3. 更新
  4.   
  5. 删除集合元素
  6.   
  7. 插入集合元素
  8.   
  9. 按照执行顺序删除
  10.   

当具有唯一约束时,了解此顺序非常重要,特别是如果您想要替换一对多的子项(删除旧/插入新的),但旧的和新的子项共享相同的唯一约束(例如相同的电子邮件地址)。在这种情况下,您可以更新旧条目,而不是删除/插入,或者您可以在删除后刷新,然后继续插入。有关更详细的示例,您可以查看this article

请注意,它未指定更新顺序。检查Hibernate代码会让我认为更新顺序将取决于实体添加到持久化上下文的顺序, NOT 它们的更新顺序。这可能在您的代码中是可预测的,但是阅读Hibernate代码并没有让我觉得我会依赖于这种排序。

我能想到三种解决方案:

  1. 尝试将hibernate.order_updates设为 true 。这应该有助于避免在更新同一个表中的多个行时出现死锁,但不会帮助解决多个表中的死锁问题。
  2. 在进行任何更新之前,让您的交易对其中一个实体进行PESSIMISTIC_WRITE锁定。您使用哪个实体将取决于您的具体情况,但只要您确保在存在死锁风险时一致地选择实体,这将阻止事务的其余部分,直到可以获得锁定。
  3. 编写代码以便在发生时捕获死锁并以合理的方式重试。管理死锁重试的组件必须位于当前事务边界之外。这是因为必须关闭失败的会话并滚动关联的事务。在this article中,您可以找到自动重试AOP方面的示例。

答案 1 :(得分:-1)

关于第一个例子 - 这种东西由数据库处理(更多地了解事务隔离级别和数据库的锁定策略)。有许多不同的方法可以处理。

对于Hibernate,javadoc到org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(EventSource)说:

以特殊顺序执行所有SQL和二级缓存更新,以便不会违反外键约束:

  1. 按照执行顺序插入
  2. 更新删除集合元素
  3. 插入集合元素
  4. 按照执行顺序删除
  5. 我认为这是Hibernate执行的SQL查询的唯一优化。其余问题由数据库处理。

相关问题