不,我不认为你缺少任何基本的东西。将数据库 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问题,其中有很多关于不同方法的讨论。