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

问题描述 投票:4回答:3

可爱的等号和哈希码,所有的理论都是herehere

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

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

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

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

在我的许多域对象中,唯一的id几乎是唯一合适的字段(Person,Address,Pet,... Customer等等??组合字段是一个好主意,但从不使用自动生成的id,我想,不是很好的建议。

我错过了别的什么吗?

java hibernate equals hashcode effective-java
3个回答
3
投票

你应该在Hibernate Community Wiki上阅读Equals and HashCode

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

如果你知道你不会参与这种情况,而且你确保它有很好的文档记录,你可能会很好。您可以随时更改实施。


1
投票

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

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

Hibernate社区通常建议使用equals和hashCode的业务键,但这种方法也会遇到相同的可变直到初始化问题。如果您的实体是Hibernate急切加载的持久集的成员,那么它可以在其字段初始化之前添加到集合中,因此基于这些字段的hashCode也不起作用。看到这个Hibernate bug report讨论业务密钥相等的问题。

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

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

另请参阅另一个stackoverflow question,其中讨论了不同的方法。


0
投票

我遇到了auto-id-generation的equals问题,因为我根据新的用户输入创建了我的对象。所以我没有明确地比较它们或者什么,我只是将它们放入Set中。那时它已经失败了,因为Set依赖于equals()/ hashCode()(它再次基于对象id,因为我没有其他选项)。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.