4

可爱的等号和哈希码,所有的理论都在这里这里

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

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

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

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

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

我错过了其他东西吗?

4

3 回答 3

5

您应该阅读Hibernate Community Wiki 上的Equals 和 HashCode 。

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

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

于 2011-09-28T07:35:46.567 回答
1

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

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

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

James Brundege建议在应用程序代码中分配 ID 以避免此问题。Lance Arlaus使用分配 ID 的对象工厂也有类似的方法。

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

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

于 2013-09-12T22:32:53.303 回答
0

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

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

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

于 2011-09-28T07:33:49.147 回答