让我们先忘掉Hibernate。假设我有两张桌子,A& B.两个事务正在更新这两个表中的相同记录,但是txn 1更新B然后更新A,而txn 2更新A然后更新B.这是典型的死锁示例。避免这种情况的最常见方法是预先定义获取资源的顺序。例如,我们应该更新表A,然后更新B.
回到Hibernate。当我们在一个会话中更新大量实体时,一旦我刷新会话,不同实体的更改将为DB生成相应的插入/更新/删除语句。 Hibernate是否有一些算法来决定实体之间的更新顺序?如果没有,Hibernate用于防止第1段中描述的死锁情况的方式是什么?
如果Hibernate维持订单,我如何知道或控制订单?我不希望我的数据库中的显式更新与Hibernate冲突,并导致死锁。
答案 0 :(得分:35)
您描述的问题不是由数据库处理的,根据我的经验,Hibernate也没有完全处理。
你必须采取明确的措施来避免这是一个问题。
Hibernate为您完成了一些工作。根据之前的回答,Hibernate确保在隔离的刷新内对插入,删除和更新进行排序,以确保它们以可实现的顺序应用。请参阅AbstractFlushingEventListener类中的performExecutions(EventSource session):
以特殊顺序执行所有SQL(和二级缓存更新),以便不会违反外键约束:
- 按照执行顺序插入
- 更新
- 删除集合元素
- 插入集合元素
- 按照执行顺序删除
醇>
当具有唯一约束时,了解此顺序非常重要,特别是如果您想要替换一对多的子项(删除旧/插入新的),但旧的和新的子项共享相同的唯一约束(例如相同的电子邮件地址)。在这种情况下,您可以更新旧条目,而不是删除/插入,或者您可以在删除后刷新,然后继续插入。有关更详细的示例,您可以查看this article。
请注意,它未指定更新顺序。检查Hibernate代码会让我认为更新顺序将取决于实体添加到持久化上下文的顺序, NOT 它们的更新顺序。这可能在您的代码中是可预测的,但是阅读Hibernate代码并没有让我觉得我会依赖于这种排序。
我能想到三种解决方案:
答案 1 :(得分:-1)
关于第一个例子 - 这种东西由数据库处理(更多地了解事务隔离级别和数据库的锁定策略)。有许多不同的方法可以处理。
对于Hibernate,javadoc到org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(EventSource)说:
以特殊顺序执行所有SQL和二级缓存更新,以便不会违反外键约束:
我认为这是Hibernate执行的SQL查询的唯一优化。其余问题由数据库处理。