使用JPA和Hibernate时应该如何实现equals和hashcode

时间:2009-10-28 17:15:00

标签: java hibernate orm equals hashcode

如何在Hibernate中实现模型类的equals和hashcode?常见的陷阱是什么?对于大多数情况,默认实现是否足够好?使用商业密钥有什么意义吗?

在我看来,在考虑延迟提取,id生成,代理等时,很难在任何情况下都能正常工作。

8 个答案:

答案 0 :(得分:63)

Hibernate对{/ 3}}

中何时/如何覆盖equals() / hashCode()有很好的描述

它的要点是,如果您的实体将成为Set的一部分,或者您将要分离/附加其实例,则只需要担心它。后者并不常见。前者通常最好通过以下方式处理:

  1. 基于商业密钥设置equals() / hashCode() - 例如属性的唯一组合,在对象(或至少是会话)生命周期内不会发生变化。
  2. 如果上述情况不可能,则在主键上设置equals() / hashCode(),如果设置为System.identityHashCode(),则对象标识为hashCode()。这里的重要部分是,您需要在添加新实体并坚持后重新加载您的设置;否则您最终可能会遇到奇怪的行为(最终导致错误和/或数据损坏),因为您的实体可能被分配到与其当前{{1}}不匹配的存储桶。

答案 1 :(得分:33)

我不认为接受的答案是准确的。

回答原来的问题:

  

对于大多数情况,默认实现是否足够好?

答案是肯定的,在大多数情况下都是。

如果实体将用于equals()(这是非常常见的) AND ,则您只需要覆盖hashcode()Set hibernate会话分离,然后重新附加到hibernate会话(这是一种不常见的hibernate用法)。

接受的答案表明,如果 条件为真,则需要覆盖这些方法。

答案 2 :(得分:12)

当通过延迟加载加载实体时,它不是基类型的实例,而是由javassist生成的动态生成的子类型,因此对同一类类型的检查将失败,因此不要使用:

if (getClass() != that.getClass()) return false;

改为使用:

if (!(otherObject instanceof Unit)) return false;

这也是一种很好的做法,如Implementing equals in Java Practices所述。

出于同样的原因,直接访问字段,可能无法工作并返回null,而不是基础值,因此不要在属性上使用比较,而是使用getter,因为它们可能会触发加载基础值。

答案 3 :(得分:10)

最佳equals / hashCode实施是在您使用unique business key时。

商家密钥应该在所有entity state transitions(暂时的,附加的,分离的,已删除的)中保持一致,这就是为什么您不能依赖id进行平等的原因。

另一种选择是切换到使用由应用程序逻辑分配的UUID identifiers。这样,您可以将UUID用于equals / hashCode,因为在刷新实体之前已分配了ID。

您甚至可以使用equalshashCode的实体标识符,但这要求您始终返回相同的hashCode值,以确保实体hashCode值一致跨所有实体状态转换。查看this post for more on this topic

答案 4 :(得分:6)

是的,这很难。在我的项目中,equals和hashCode都依赖于对象的id。这个解决方案的问题是,如果对象尚未持久化,它们都不起作用,因为id是由数据库生成的。在我的情况下,这是可以容忍的,因为在几乎所有情况下,对象都会立即存在。除此之外,它工作得很好并且易于实现。

答案 5 :(得分:2)

如果您碰巧覆盖equals,请确保履行合同: -

  • SYMMETRY
  • REFLECTIVE
  • TRANSITIVE
  • 一致的
  • NON NULL

并覆盖hashCode,因为其合同依赖于equals实施。

Joshua Bloch(Collection框架的设计者)强烈敦促遵守这些规则。

  • 第9项:覆盖等于
  • 时始终覆盖hashCode

如果您不遵守这些合同,会产生严重的意外影响。例如,List.contains(Object o)可能会返回错误的boolean值,因为未达到一般合同。

答案 6 :(得分:1)

在Hibernate 5.2的文档中,它表示你可能根本不想实现hashCode和equals - 具体取决于你的情况。

https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode

通常,如果在数据库中它们相等(不实现hashCode和equals),则从同一会话加载的两个对象将是相等的。

如果您正在使用两个或更多会话,则会变得复杂。在这种情况下,两个对象的相等性取决于你的equals-method实现。

此外,如果您的equals-method正在比较仅在第一次持久保存对象时生成的ID,则会遇到麻烦。当调用equals时,它们可能不存在。

答案 7 :(得分:0)

这里有一篇非常好的文章:https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

引用文章中的重要一行:

  

我们建议使用Business key实现equals()和hashCode()   平等。业务键平等意味着equals()方法   仅比较形成业务键的属性,即键   将识别我们在现实世界中的实例(一个自然的候选人   键):

简单来说

public class Cat {

...
public boolean equals(Object other) {
    //Basic test / class cast
    return this.catId==other.catId;
}

public int hashCode() {
    int result;

    return 3*this.catId; //any primenumber 
}

}