JPA中的hashCode() / equals()困境

395
在这里已经有关于JPA实体的一些讨论,包括应该为JPA实体类使用哪个hashCode()/equals()实现。大多数(如果不是全部)依赖于Hibernate,但我想讨论JPA实现中性的问题(顺便说一下,我正在使用EclipseLink)。
所有可能的实现都有自己的优点缺点,涉及到以下方面:
  • hashCode()/equals()合同符合性(不可变性)适用于List/Set操作
  • 是否可以检测到相同对象(例如来自不同会话、从惰性加载的数据结构中的动态代理)
  • 实体在分离(或未持久化)状态下是否表现正确
就我所看到的,有三种选择
  1. 不要覆盖它们;依赖于Object.equals()Object.hashCode()
    • hashCode()/equals()有效
    • 无法识别相同的对象,动态代理存在问题
    • 与分离实体没有问题
  2. 根据主键覆盖它们
    • hashCode()/equals()被破坏了
    • 正确的标识(对于所有受控实体)
    • 与分离实体存在问题
  3. 根据业务ID(非主键字段;外键怎么办?)覆盖它们
    • hashCode()/equals()被破坏了
    • 正确的标识(对于所有受控实体)
    • 与分离实体没有问题

我的问题是:

  1. 我是否错过了任何选项和/或优缺点?
  2. 您选择了哪个选项以及为什么?



更新1:

我所谓的“hashCode()/ equals()已损坏”,是指连续的hashCode()调用可能返回不同的值,这在正确实现时并非Object API文档中所述的“损坏”,但当尝试从Map、Set或其他基于哈希的Collection检索更改的实体时会导致问题。因此,JPA实现(至少是EclipseLink)在某些情况下将无法正常工作。
更新2:
感谢您的回答 - 其中大部分质量很高。不幸的是,我仍然不确定哪种方法对于实际应用程序最好,或者如何确定我的应用程序的最佳方法。因此,我将保持问题开放,并希望进行更多讨论和/或意见。

4
“hashCode()/equals() broken” 是什么意思,我不明白。 - nanda
5
那么,如果你在选项2和3中使用相同的策略来实现equals()和hashCode(),它们就不会以那种意义上被“破坏”。 - matt b
14
选项3并非如此。hashCode()和equals()应该使用相同的标准,因此,如果您的某个字段更改了,hashcode()方法将为相同实例返回一个不同的值,而equals()方法也将是如此。您已经省略了hashcode() javadoc中第二部分的内容:在Java应用程序的执行过程中多次调用同一对象的hashCode方法时,只要没有修改用于比较对象的equals信息,hashCode方法必须始终返回相同的整数。 - matt b
2
实际上,该句子的这一部分意思是相反的 - 在同一对象实例上调用hashcode()应该返回相同的值,除非在equals()实现中使用的任何字段发生更改。换句话说,如果您的类中有三个字段,并且您的equals()方法仅使用其中两个来确定实例的相等性,则可以期望hashcode()返回值在更改其中一个字段的值时发生更改 - 当您考虑到此对象实例不再“等于”旧实例所代表的值时,这是有意义的。 - matt b
2
尝试从Map、Set或其他基于哈希的集合中检索更改的实体时可能会出现问题,应该是"尝试从HashMap、HashSet或其他基于哈希的集合中检索更改的实体时可能会出现问题"。 - nanda
显示剩余16条评论
21个回答

-2

以下是Scala的一个简单(并经过测试)解决方案。

  • 请注意,此解决方案不适用于问题中给出的任何3个类别之一。

  • 我的所有实体都是UUIDEntity的子类,因此我遵循不重复自己(DRY)原则。

  • 如果需要,可以通过使用更多伪随机数使UUID生成更精确。

Scala代码:

import javax.persistence._
import scala.util.Random

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class UUIDEntity {
  @Id  @GeneratedValue(strategy = GenerationType.TABLE)
  var id:java.lang.Long=null
  var uuid:java.lang.Long=Random.nextLong()
  override def equals(o:Any):Boolean= 
    o match{
      case o : UUIDEntity => o.uuid==uuid
      case _ => false
    }
  override def hashCode() = uuid.hashCode()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接