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

问题描述 投票:87回答:8

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

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

java hibernate orm equals hashcode
8个回答
63
投票

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

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

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

33
投票

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

回答原来的问题:

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

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

你只需要覆盖equals()hashcode(),如果实体将在Set中使用(这是非常常见的)并且实体将与hibernate会话分离,并随后重新附加到hibernate会话(这是一种不常见的hibernate用法)。

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


12
投票

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

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

改为使用:

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

这也是一个很好的做法,正如Implementing equals in Java Practices所解释的那样。

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


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


6
投票

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


2
投票

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

  • 对称
  • 反光
  • 传递性
  • 一致的
  • 不是NULL

并覆盖hashCode,因为它的合同依赖于equals实现。

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

  • 第9项:覆盖equals时始终覆盖hashCode

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


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时,它们可能不存在。


0
投票

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

引用文章中的一条重要内容:

我们建议使用Business key equality实现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 
}

}
© www.soinside.com 2019 - 2024. All rights reserved.