在 equals 和 hashcode 方法中使用自动生成的 Hibernate 实体对象的 id

Posted

技术标签:

【中文标题】在 equals 和 hashcode 方法中使用自动生成的 Hibernate 实体对象的 id【英文标题】:Using auto generated id of Hibernate entity object in the equals and hashcode methods 【发布时间】:2011-11-26 14:56:08 【问题描述】:

可爱的equals和hashcode,所有的理论都是here和here

我已决定在我的许多休眠实体/域对象中使用 equals() 和 hashcode() 中自动生成的 id。

但是,许多网站表示您不应该这样做,因为在比较或使用哈希码的过程中第一次将对象持久化到数据库中的风险。

我的观点是,在大多数用例中,这比任何其他字段更不可能被更改。

单个域对象在首次创建时生成一次 id,而几乎所有其他字段都有机会在正常业务流程中进行更改(甚至可以更改唯一的用户名...)。

在我的许多域对象中,唯一 id 几乎是唯一需要考虑的字段(人、地址、宠物、... 客户等等等?组合字段是个好主意,但是 从不 我认为使用自动生成的 id 不是一个好建议。

我还缺少什么吗?

【问题讨论】:

【参考方案1】:

您应该阅读 Hibernate 社区 Wiki 上的 Equals and HashCode。

equals 中不使用数据库标识符的主要原因,暗示hashCode 是为了处理存储的但不是持久的实体。在持久化之前,您的所有实例都将是equal,除非您注意明确地处理这种情况。

如果您知道自己不会出现这种情况,并且确保它有详细的文档记录,那么您可能会没事的。您可以随时更改实现。

【讨论】:

【参考方案2】:

不,我认为您没有遗漏任何基本内容。将数据库 ID 用于 equals 和 hashCode 问题的“根本原因”是 Hibernate 生成的 ID 存储在可变字段中,并且当 Hibernate 分配 ID 时,该字段的值会发生变化。 Equals 和 hashCode 应该基于不可变状态,如 Odersky / Spoon / Venners article 中关于编写 equals / hashCode 方法的“陷阱 #3”所述。这意味着,除其他事项外,您不能将实例添加到 Set 或将其与另一个实例进行比较,直到它被持久化,当 hashCode 变得固定时。

您可能缺少的唯一一件事是跟踪 ID 的分配时间是多么棘手,因为它是由 Hibernate 自动完成的。当然,您可能永远不会调用setId(someNewId),但您可能会发出一个触发会话刷新的查询,并且一大堆与查询无关的临时实体的 ID 突然从空变为非空。

Hibernate 社区通常建议对 equals 和 hashCode 使用业务密钥,但这种方法也存在同样的 mutable-until-initialized 问题。如果您的实体是由 Hibernate 预先加载的持久集的成员,则可以在其字段初始化之前将其添加到集合中,因此基于这些字段的 hashCode 也不起作用。请参阅此 Hibernate bug report 讨论业务密钥平等问题。

James Brundege recommends 在应用程序代码中分配 ID 以避免此问题。 Lance Arlaus 有一个 similar approach 使用分配 ID 的对象工厂。

如果您真的想为 equals 和 hashCode 使用自动生成的 ID,请考虑在 equals 和 hashCode 的实现中使用 Assert.notNull(id) 来检测编码错误。

另请参阅另一个 *** question,其中有很多关于不同方法的讨论。

【讨论】:

【参考方案3】:

我遇到了 auto-id-generation 的 equals 问题,因为 一旦根据新的用户输入创建对象,我就将它们放入 Set 中。 所以我没有明确比较它们或其他东西,我只是将它们放入一个集合中。 那时它已经失败了,因为 Set 依赖于 equals()/hashCode() ( 再次基于对象 ID,因为我别无选择)。

然后我将其更改为自己实际生成和分配 id,这是更好的解决方案。

请阅读这篇文章,它准确地解释了问题: dont-let-hibernate-steal-your-identity

【讨论】:

以上是关于在 equals 和 hashcode 方法中使用自动生成的 Hibernate 实体对象的 id的主要内容,如果未能解决你的问题,请参考以下文章

在 equals 和 hashcode 方法中使用自动生成的 Hibernate 实体对象的 id

hashmap中equals() 和hashcode(),请教

Java 中 hashCode 和 equals 方法是什么?它们和 == 各有什么区别?

java 集合中重写hashCode方法和重写equals方法啥关系?

详解equals()方法和hashCode()方法

Java中equals和hashcode的区别?