为什么会话外的Hibernate实体应该实现equals()和hashcode()?

时间:2015-12-30 16:54:26

标签: java hibernate orm equals hashcode

Hibernate用户指南的第2.1.5节说明了

  

如果您要处理Session之外的实体(无论是瞬态还是分离)......您应该考虑实现equals / hashCode。

忽略在诸如Set之类的集合中使用这些实体的明显原因,我们希望或需要覆盖hibernate doc中建议的现有equals / hashcode的其他情况是什么?

3 个答案:

答案 0 :(得分:1)

首先,实体相等是一个域细节,最好从业务代码中抽象出来。显而易见的最佳位置是实体本身。此外,相等规则通常可能基于实体状态或其他域规则而有所不同,因此覆盖equals方法可确保这些规则不仅适用于业务代码,还适用于集合使用和hibernate的会话管理。

考虑一个基于复合键的实体,它使用几个字段来构成平等。以下哪些业务代码看起来更具吸引力,并且变化最不严格:

public void someBusinessFunction(EntityA e1, EntityA e2) {
  if(e1.equals(e2))
    throw new SomeEqualityException();
  // do logic
}

public void someBusinessFunction2(EntityA e1, EntityA e2) {
  if(e1.getId().getFieldA().equals(e2.getId().getFieldA()) &&
       e1.getId().getFieldB().equals(e2.getId().getFieldB()) &&
       e1.getId().getFieldC().equals(e2.getId().getFieldC())) {
    throw new SomeEqualityException();
  }
  // do logic
}

从业务代码的角度来看,我们不应该关心什么是平等。所有代码都关心的是同一个类的2个实例是否彼此相等。使用什么规则来确定用例是无关紧要的。

简而言之,通过抽象应该首先抽象的细节,这只是一个很好的编程。

修改

更具体的是Java语言本身,Object的股票相等性仅比较两个操作数是否通过==运算符指向同一物理实例。仅当两个实例指向相同的内存位置时,此选项才有效。

可以通过多个事务获得相同的域实体,因此可以指向两个不同的内存位置,因为它们曾经由不同的会话管理。在这种情况下,默认的equals方法将返回false,这可能是不合需要的。

通过重写equals方法,您可以明确地确保上述情况始终返回true的相等性,无论实例是如何获得的以及它可以存储在内存中的位置。

此外,java doc明确指出当覆盖hashCode以维持equalshashCode之间的契约时,覆盖equals会发生,以便在对象相等为真时,哈希码也是相同的。

答案 1 :(得分:1)

看起来2.1.5不是一个非常明确的部分。

  

如果您要处理Session之外的实体(无论是瞬态还是分离)......您应该考虑实现equals / hashCode。

它并不意味着"如果你打算使用重新附加的分离实例"你必须覆盖equals()和hashCode()方法。

这意味着其他的事情。如果从会话中获得具有相同id的两个对象,则它将是相同的对象(在内存中)。

Set<Person> set = new HashSet<Person>();
Session session = factory.openSession();

Person p1 = session.get( Person.class, 1 );
Person p2 = session.get( Person.class, 1);

set.put(p1);
set.put(p2);

session.close();

由于setp1 == p2使用默认Personequals()hashCode()将是一个对象。

如果从两个会话中获得两个具有相同id的对象,则内存中将有两个对象。

Set<Person> set = new HashSet<Person>();

Session session1 = factory.openSession();
Person p1 = session1.get( Person.class, 1 );
session1.close();

Session session2 = factory.openSession();
Person p2 = session1.get( Person.class, 1 );
session2.close();

set.put(p1);
set.put(p2);

由于setp1 != p2将是两个对象。

2.1.5中的文档只是警告您,如果您想在集合(或其他此类集合)的会话之外使用持久对象,则需要实现equals()hashCode()或制作其他(!)努力获得正确的工作集合。

答案 2 :(得分:0)

如果您目前不在Set(或Collection中使用您的已分离实体,请考虑remove(Object)及类似情况依赖于equals方法的方法,并不意味着您(或您的同事)在您忘记equals被破坏的六个月后不会将这些实体放入集合中。这对我来说是个好理由。