JPA hashCode() / equals() 困境

2022-08-31 04:43:43

这里已经一些关于JPA实体的讨论,以及哪些/实现应该用于JPA实体类。它们中的大多数(如果不是全部)都依赖于Hibernate,但我想以JPA实现中立的方式讨论它们(顺便说一句,我正在使用EclipseLink)。hashCode()equals()

所有可能的实现在以下方面都有自己的优点缺点

  • hashCode()/equals() contract conformity (immutability) for / operationsListSet
  • 是否可以检测到相同的对象(例如,来自不同会话,来自延迟加载的数据结构的动态代理)
  • 实体在分离(或非持久)状态下的行为是否正确

据我所知,有三个选项

  1. 不要覆盖它们;依靠和Object.equals()Object.hashCode()
    • hashCode()/equals()工作
    • 无法识别相同的对象,动态代理存在问题
    • 分离实体没有问题
  2. 根据主键覆盖它们
    • hashCode()/equals()已损坏
    • 正确的标识(适用于所有托管实体)
    • 分离实体的问题
  3. 根据业务 ID(非主键字段;外键呢?)覆盖它们
    • hashCode()/equals()已损坏
    • 正确的标识(适用于所有托管实体)
    • 分离实体没有问题

我的问题是:

  1. 我是否错过了一个选项和/或赞成/反对点?
  2. 您选择了什么选项,为什么?



更新 1:

通过“/ 被破坏”,我的意思是连续调用可能会返回不同的值,这些值(当正确实现时)在API文档的意义上没有被破坏,但是当尝试从中检索已更改的实体或其他基于哈希的实体时,这会导致问题。因此,JPA实现(至少是EclipseLink)在某些情况下将无法正常工作。hashCode()equals()hashCode()ObjectMapSetCollection

更新 2:

感谢您的回答 - 其中大多数都有非凡的质量。
不幸的是,我仍然不确定哪种方法最适合实际应用程序,或者如何为我的应用程序确定最佳方法。因此,我将保持问题的开放性,并希望有更多的讨论和/或意见。


答案 1

阅读这篇关于这个主题的非常好的文章:不要让冬眠偷走你的身份

文章的结论是这样的:

当对象持久化到数据库时,对象标识似乎很难正确实现。但是,问题完全源于允许在保存对象之前在没有 ID 的情况下存在对象。我们可以通过将对象ID从对象关系映射框架(如Hibernate)中移开来来解决这些问题。相反,可以在实例化对象后立即分配对象 ID。这使得对象标识变得简单且无错误,并减少了域模型中所需的代码量。


答案 2

我总是覆盖等于/哈希码,并根据业务ID实现它。对我来说似乎是最合理的解决方案。请参阅以下链接

为了总结所有这些东西,下面列出了使用不同的方法来处理equals/hashCode,什么会起作用或不起作用:enter image description here

编辑

为了解释为什么这对我有用:

  1. 我通常不在我的JPA应用程序中使用基于哈希的集合(HashMap/HashSet)。如果必须,我更喜欢创建 UniqueList 解决方案。
  2. 我认为在运行时更改业务 ID 不是任何数据库应用程序的最佳实践。在没有其他解决方案的极少数情况下,我会做一些特殊的处理,比如删除元素并将其放回基于哈希的集合中。
  3. 对于我的模型,我在构造函数上设置了业务 ID,并且不为其提供 setter。我让 JPA 实现更改字段而不是属性。
  4. UUID解决方案似乎有些过分。如果您有自然的业务 ID,为什么选择 UUID?毕竟,我会在数据库中设置业务ID的唯一性。那么,为什么数据库中的每个表都有三个索引呢?

推荐