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

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

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

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

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

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

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

我错过了别的什么吗?

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

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

如果您知道自己不会参与这种情况,并且确保它有详细记录,那么您可能会很好。 您可以随时更改实施。

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

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

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

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

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

另请参阅另一个stackoverflow 问题,其中包含对不同方法的大量讨论。

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

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

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