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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1638723/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 18:05:42  来源:igfitidea点击:

How should equals and hashcode be implemented when using JPA and Hibernate

javahibernateormequalshashcode

提问by egaga

How should model class's equals and hashcode be implemented in Hibernate? What are the common pitfalls? Is the default implementation good enough for most cases? Is there any sense to use business keys?

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

It seems to me that it's pretty hard to get it right to work in every situation, when lazy fetching, id generation, proxy, etc are taken into account.

在我看来,当考虑到延迟获取、id 生成、代理等时,很难在每种情况下都正确工作。

采纳答案by ChssPly76

Hibernate has a nice and long description of when / how to override equals()/ hashCode()in documentation

Hibernate 对何时/如何覆盖equals()/hashCode()文档中有一个很好的长描述

The gist of it is you only need to worry about it if your entity will be part of a Setor if you're going to be detaching / attaching its instances. The latter is not that common. The former is usually best handled via:

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

  1. Basing equals()/ hashCode()on a business key - e.g. a unique combination of attributes that is not going to change during object (or, at least, session) lifetime.
  2. If the above is impossible, base equals()/ hashCode()on primary key IF it's set and object identity / System.identityHashCode()otherwise. The importantpart here is that you need to reloadyour Set after new entity has been added to it and persisted; otherwise you may end up with strange behavior (ultimately resulting in errors and / or data corruption) because your entity may be allocated to a bucket not matching its current hashCode().
  1. 基于equals()/hashCode()基于业务键 - 例如,在对象(或至少会话)生命周期内不会更改的唯一属性组合。
  2. 如果以上是不可能的,则基于equals()/hashCode()基于主键,如果它已设置,并且对象标识/System.identityHashCode()否则。这里的重要部分是您需要在新实体添加并持久化后重新加载您的 Set;否则您最终可能会出现奇怪的行为(最终导致错误和/或数据损坏),因为您的实体可能被分配到与其当前hashCode().

回答by Carlos

Yeah, it's hard. In my project equals and hashCode both rely on the id of the object. The problem of this solution is that neither of them works if the object has not been persisted yet, as the id is generated by database. In my case that's tolerable since in almost all cases objects are persisted right away. Other than that, it works great and is easy to implement.

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

回答by Phil

I don't think that the accepted answer is accurate.

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

To answer the original question:

要回答原始问题:

Is the default implementation good enough for most cases?

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

The answer is yes, in most cases it is.

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

You only need to override equals()and hashcode()if the entity will be used in a Set(which is very common) ANDthe entity will be detached from, and subsequently re-attached to, hibernate sessions (which is an uncommon usage of hibernate).

你只需要重写equals()hashcode()该实体将被用在Set(这是很常见的)实体将被分离,并随后重新附着,休眠会话(这是休眠的一种罕见的使用)。

The accepted answer indicates that the methods need to be overriden if eithercondition is true.

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

回答by stivlo

When an entity is loaded through lazy loading, it's not an instance of the base type, but is a dynamically generated subtype generated by javassist, thus a check on the same class type will fail, so don't use:

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

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

instead use:

而是使用:

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

which is also a good practice, as explained on Implementing equals in Java Practices.

这也是一种很好的做法,如在 Java 实践中实现等式所述

for the same reason, accessing directly fields, may not work and return null, instead of the underlying value, so don't use comparison on the properties, but use the getters, since they might trigger to load the underlying values.

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

回答by Vlad Mihalcea

The best equals/hashCodeimplementation is when you use a unique business key.

最好的equals/hashCode实现是当您使用唯一的业务密钥时

The business key should be consistent across all entity state transitions(transient, attached, detached, removed), that's why you can't rely on id for equality.

业务键应该在所有实体状态转换(瞬态、附加、分离、删除)中保持一致,这就是您不能依赖 id 来实现平等的原因。

Another option is to switch to using UUID identifiers, assigned by the application logic. This way, you can use the UUID for the equals/hashCodebecause the id is assigned before the entity gets flushed.

另一种选择是切换到使用UUID 标识符,由应用程序逻辑分配。这样,您可以为equals/使用 UUID,hashCode因为 id 是在实体被刷新之前分配的。

You can even use the entity identifier for equalsand hashCode, but that requires you to always return the same hashCodevalue so that you make sure that the entity hashCode value is consistent across all entity state transitions. Check out this post for more on this topic.

您甚至可以为equalsand使用实体标识符hashCode,但这要求您始终返回相同的hashCode值,以便确保实体 hashCode 值在所有实体状态转换中保持一致。有关此主题的更多信息,请查看此帖子

回答by Ravi Shekhar

There is very nice article here: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

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

Quoting an important line from the article:

引用文章中的重要一句话:

We recommend implementing equals() and hashCode() using Business key equality. Business key equality means that the equals() method compares only the properties that form the business key, a key that would identify our instance in the real world (a natural candidate key):

我们建议使用业务键相等来实现 equals() 和 hashCode()。业务键相等意味着 equals() 方法只比较构成业务键的属性,该键可以在现实世界中识别我们的实例(自然候选键):

In simple terms

简单来说

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 
}

}

回答by Awan Biru

If you happened to override equals, make sure you fulfill its contracts:-

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

  • SYMMETRY
  • REFLECTIVE
  • TRANSITIVE
  • CONSISTENT
  • NON NULL
  • 对称
  • 反光的
  • 传递性
  • 持续的
  • 非空

And override hashCode, as its contract rely on equalsimplementation.

和 override hashCode,因为它的合约依赖于equals实现。

Joshua Bloch(designer of Collection framework) strongly urged these rules to be followed.

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

  • item 9: Always override hashCode when you override equals
  • 第 9 项:在覆盖 equals 时始终覆盖 hashCode

There are serious unintended effect when you don't follow these contracts. For example List#contains(Object o)might return wrong booleanvalue as the general contract not fulfilled.

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

回答by Nina

In the documentation of Hibernate 5.2 it says you might not want to implement hashCode and equals at all - depending on your situation.

在 Hibernate 5.2 的文档中,它说您可能根本不想实现 hashCode 和 equals - 取决于您的情况。

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

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

Generally, two objects loaded from the same session will be equal if they are equal in the database (without implementing hashCode and equals).

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

It gets complicated if you're using two or more sessions. In this case, the equality of two objects depends on your equals-method implementation.

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

Further, you'll get into trouble if your equals-method is comparing IDs that are only generated while persisting an object for the first time. They might not be there yet when equals is called.

此外,如果您的 equals 方法比较仅在第一次持久化对象时生成的 ID,您就会遇到麻烦。当调用 equals 时,它们可能还不存在。