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

时间:2011-09-28 06:59:14

标签: java hibernate equals hashcode effective-java

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

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

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

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

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

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

我错过了其他什么吗?

3 个答案:

答案 0 :(得分:3)

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

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

如果您知道自己不会参与这种情况,并确保记录良好,那么您可能会感觉良好。您可以随时更改实施。

答案 1 :(得分: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,其中讨论了不同的方法。

答案 2 :(得分:0)

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

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

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