【问题标题】:JPA : not overriding equals() and hashCode() in the entities?JPA:不覆盖实体中的equals()和hashCode()?
【发布时间】:2011-07-08 15:56:18
【问题描述】:

阅读 this article 后,我倾向于不完全覆盖 equals() 和 hashCode()。

在那篇文章的摘要中,关于 no eq/hC at all 列,唯一的后果是我无法进行比较操作,例如:

  1. contains() 在分离实体的列表中,或
  2. 比较来自不同会话的相同实体

并期待正确的结果。

但我仍然有疑问,想问问你的经验,完全跳过 equals 和 hashCode 是否是一种不好的做法,以及我现在还不知道的其他后果。

只是另一个信息点,我倾向于使用列表集合而不是集合。我的假设是,在存储在列表中时,我真的不需要覆盖 hashCode 和 equal。

【问题讨论】:

  • 之前有人问过这个问题:Should I write equals() methods in JPA entities?。我的回答是:应该实现hashCode()equals(),但不使用@Id 属性。 (其他人有不同的意见:-))
  • 你好!是的,我在发布我的之前就知道你以前的帖子,因为我确实在谷歌上搜索过类似的问题。与我的帖子的不同之处在于,您想知道在不定义 equal() 的情况下这是否可行,我的问题更多是在不定义它们的情况下我会失去什么。对于您帖子中的好答案,更多的是关于如何定义equals()和hashCode()的每种方式的利弊
  • 这里有一个很好的总结:burtbeckwith.com/blog/?p=53
  • 如果您不想覆盖,那么您可以应用不同的自定义集合/比较器,它们有时会查看整个对象,有时只是查看主键,在适合您的上下文之后。但这也不是免费的午餐..

标签: java hibernate jpa jpa-2.0


【解决方案1】:

阅读这篇关于这个主题的非常好的文章:Don't Let Hibernate Steal Your Identity

文章的结论是这样的:

在以下情况下,对象标识很难正确实现 对象被持久化到数据库中。然而,问题源于 完全是因为允许对象在没有 id 的情况下存在 保存。我们可以通过承担责任来解决这些问题 从对象关系映射框架中分配对象 ID 比如休眠。相反,对象 ID 可以在 对象被实例化。这使得对象标识简单并且 无错误,并减少领域模型所需的代码量。

【讨论】:

  • 旧文章,但非常好!它不仅解释了为什么我们需要重写,还提供了不同的方法来实现它们。我想我会坚持使用 UUID 生成方法。
  • 这就是我为刚刚开始的项目所做的;我喜欢这种方法,感觉更干净。
【解决方案2】:

完全跳过equals和hashCode是否是一种不好的做法

是的。你应该总是覆盖你的equals和hashCode。时期。原因是这个方法已经存在于你的类中,在 Object 中实现。事实证明,这个实现是通用的,几乎 100% 的情况下,对于您自己的对象来说,它是一个错误的实现。因此,通过跳过 equals/hashCode 您实际上提供了错误的实现,并且(在最好的情况下)会混淆使用这些类的人。可能是您的同事,也可能是您正在使用的某个框架(这可能会导致无法预测且难以调试的问题)。

没有理由实施这些方法。大多数 IDE 提供了 equals/hashCode 的生成器。您只需将您的业务密钥告知 IDE。

【讨论】:

  • 您好!感谢您的洞察力。我知道 IDE 可以帮助生成它,并且有 jakarta common lang EqualsBuilder 或 HashCodeBuilder 。但我只是在想,如果我对默认的 Object.equals() 和 Object.hashCode() 感到满意,使用 == 表示相等,跳过它是否是个坏主意。因为一旦我决定实施 eq/hC,就会增加一些实施成本和未来的维护成本。您的论点是有道理的,但是可以更改的业务关键字段呢?我想虽然大多数业务关键领域不会改变,但也许有些可以改变?
  • 业务密钥可以更改,这很好。这就是您使用代理键的原因 :-) 但是通常,如果业务键更改以向键添加新属性,则意味着您的应用程序将被重新部署。因此,那里没有问题,因为所有现有对象都将离开 JVM 并使用新属性加载。如果一个特定的对象改变了自己的业务密钥,这意味着它不再等同于具有相同业务密钥的其他对象。这是非常合适的。因此,通过实施 equals/hashCode,您不会有任何损失。
  • 抱歉,我的最新回复不清楚。我正在考虑修改数据库中已经存在的记录。因为根据文章,在某些情况下使用eq/hC中的id对于hibernate是不够的,所以介绍了基于业务关键字段的eq/hC。所以,我在想,如果可以更改业务键值,我如何在集合中找到仍然具有旧业务键值的旧实体,然后用新值替换它?这似乎我必须使用 id 来定位旧实体并更改业务键值。
  • equals/hashCode 讨论总是很有趣,特别是在我们有时间的时候 :-) 但简而言之:您的业务密钥不应该改变。这是可以接受的(这就是你使用代理键的原因),但一般来说,它不应该。因此,如果对象的业务键发生变化,它会立即与之前具有相同业务键的实例不同。说真的,有很多关于 equals/hashCode 的资料,包括“Effective Java”和“Java Persistence with Hibernate”。
【解决方案3】:

你从那篇文章中得到的结论正好相反。

Hibernate 严重依赖 equals 的正确实现。否则会出现故障。

事实上,几乎所有事情都可以;包括标准的 java 集合。

使用持久性时默认实现不起作用。您应该始终同时实现 equals 和 hashcode。关于如何做到这一点,也有一个简单的规则:

  • 对于实体,使用对象的键。
  • 对于值对象,使用值

始终确保您在 equals/hashcode 中使用的值是不可变的。如果你将它们传递出去(比如在 getter 中),最好以不可变的形式传递出去。

这个建议会改善你的生活:)

【讨论】:

  • 我目前正在为我的 id 使用代理键,这意味着,我应该在我的 eq/hC 中只使用我的 Long id。如果我错了,请纠正我,但是根据我上面发布的文章,这种方法在休眠状态下不行,因为 session.saveOrUpdate 不会自动填充生成的 id 字段,但它必须等到它被刷新或提交,这可能会导致麻烦添加到集合时。但另一方面,正如我测试过的那样,这与 JPA 一起工作得很好,因为调用 entityManager.persist 会自动填充生成的 id 字段,而无需等待刷新和提交?
  • 根据我的经验,这是hibernate工作方式的缺点之一。这就是为什么我更喜欢使用业务键作为主键的原因;它通常从一开始就可用。否则,强制休眠从一开始就为您提供密钥(通过在将项目添加到集合之前将其持久化)或手动执行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
相关资源
最近更新 更多