2011-09-28 64 views
4

可爱的equals和hashCode,所有的理论是herehere使用自动生成的hibenate实体对象的ID在equals和hashCode方法

我已用等号内的自动生成的ID的决定()和hashcode()在我的一些休眠实体/域对象。

然而,一些网站说,你永远不应该这样做,由于持续的对象到数据库中的第一次的风险,而这是在被比较或使用哈希码的过程。

我的观点是,在大多数情况下使用,这是更不可能比被改变任何其他领域。

各个领域对象有生成的ID一旦被首先创建时,而几乎所有其他领域具有在正常的业务流程,以改变(甚至是唯一的用户名可以改变...)的机会。

而在我的许多域对象中,唯一的id几乎是唯一适合的字段需要考虑(Person,Address,Pet,... Customer等等等等)?合并字段是个好主意,但从来没有使用自动生成的ID,我认为,没有好的建议。

我失去了什么东西?

回答

3

你应该在Hibernate社区维基阅读Equals and HashCode

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

如果你知道你不会是在这种情况下,你要确保它是有据可查的,你很可能确定。您可以随时更改实施。

0

我穿过等于因为我已经把事实 与自动识别代问题就来了我一旦创建基于新用户输入的对象,就将它们设置为Set。 所以我没有明确地比较它们或者什么,我只是把它们放入一个组。 在这一点上它已经失败,因为Set依靠的equals()/ hashCode()方法(这 又是基于对象ID,因为我没有其他的选择)。

然后我改变了它实际产生和分配的ID我自己,这是更好的解决方案。

请阅读这篇文章,它正好说明这个问题: dont-let-hibernate-steal-your-identity

1

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

您可能会遗漏的唯一情况是,您需要跟踪ID的分配时间,这很棘手,因为它是由Hibernate自动完成的。当然,你可能永远不会调用setId(someNewId),但是你可能发出一个触发会话刷新的查询,并且与该查询无关的一大堆瞬态实体突然将其ID从null更改为非null。

对于equals和hashCode使用业务密钥通常是Hibernate社区推荐的,但是这种方法也会遭受同样的mutable-until-initialized问题。如果你的实体是一个由Hibernate急切加载的持久集的成员,那么它可以在它的字段被初始化之前被添加到集合中,所以基于这些字段的hashCode也不起作用。参见Hibernate bug report讨论业务密钥相等的问题。

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

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

另请参阅另一个stackoverflow question与许多不同的方法讨论。

相关问题