在使用JPA和Hibernate时应该如何实现equals和hashcode
应该如何在Hibernate中实现模型类的equals和hashcode? 什么是常见的陷阱? 在大多数情况下,默认实现是否足够好? 有没有使用商业密钥的意义?
在我看来,在任何情况下都很难做到这一点,比如懒惰的抓取,身份证生成,代理等等。
Hibernate对于何时/如何覆盖文档中的 equals()
/ hashCode()
有很好的描述
它的要点是你只需要担心,如果你的实体将成为一个Set
一部分,或者如果你将要分离/附加其实例。 后者并不常见。 前者通常最好通过以下方式处理:
- 基于业务关键字的
equals()
/hashCode()
– 例如在对象(至less是会话)生命周期中不会改变的属性的唯一组合。 - 如果以上是不可能的,那么在主键上设置
equals()
/hashCode()
,如果设置了,则返回object identity /System.identityHashCode()
。 这里的重要部分是你需要重新加载你的设置新的实体已被添加到它并坚持; 否则你可能会以奇怪的行为(最终导致错误和/或数据损坏)结束,因为你的实体可能被分配到一个不符合其当前hashCode()
的桶。
我不认为接受的答案是准确的。
回答原来的问题:
在大多数情况下,默认实现是否足够好?
答案是肯定的,在大多数情况下是这样。
你只需要重写equals()
和hashcode()
如果实体将被用在一个Set
(这是很常见的) ,并且实体将从hibernate会话中分离出来,并随后重新连接到hibernate会话中(这是一种罕见的用法冬眠)。
接受的答案表明,如果任一条件为真,则方法需要被覆盖。
当通过延迟加载实体时,它不是基types的实例,而是由javassist生成的dynamic生成的子types,因此对相同类types的检查将失败,因此不要使用:
if (getClass() != that.getClass()) return false;
改为使用:
if (!(otherObject instanceof Unit)) return false;
这也是一个很好的做法,就像在Java实践中实现equals一样 。
出于同样的原因,直接访问字段可能不工作,并返回null,而不是基础值,所以不要使用比较属性,而是使用getter,因为它们可能触发加载基础值。
最好的equals
/ hashCode
实现是当你使用一个唯一的业务密钥 。
业务密钥应该在所有实体状态转换 (瞬态的,连接的,分离的,移除的)中保持一致,这就是为什么你不能依靠id来实现平等的原因。
另一种select是切换到使用由应用程序逻辑分配的UUID标识符 。 这样,您可以使用UUID作为equals
/ hashCode
因为在实体被刷新之前分配了id。
甚至可以使用equals
和hashCode
的实体标识符,但是这要求您始终返回相同的hashCode
值,以便确保实体hashCode值在所有实体状态转换中保持一致。 看看这个post的更多关于这个话题 。
是的,这很难。 在我的项目中,equals和hashCode都依赖于对象的id。 这个解决scheme的问题是,如果对象还没有被保存,它们都不工作,因为id是由数据库生成的。 在我的情况下,这是可以忍受的,因为在几乎所有的情况下,对象都被立即坚持。 除此之外,它工作的很好,很容易实现。
如果你碰巧超越equals
,确保你履行合同:
- 对称
- 反光
- 传递性
- 一致的
- 非空
并重写hashCode
,因为它的合约依赖于equals
实现。
(Collection框架的devise者)Joshua Bloch强烈地敦促遵守这些规则。
- 项目9:当您覆盖equals时总是覆盖hashCode
当你不遵守这些合同时,会产生严重的意外影响。 例如, List.contains(Object o)
可能会返回错误的boolean
值,因为一般合同没有完成。
这里有非常好的文章: https : //docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html
从这篇文章引用一条重要的路线:
我们build议使用业务键相等来实现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 } }
在Hibernate 5.2的文档中,它表示你可能不想实现hashCode和equals,这取决于你的情况。
一般来说,如果在数据库中相同(不执行hashCode和equals),则从同一会话加载的两个对象将相等。
如果您使用两个或更多会话,则会变得复杂。 在这种情况下,两个对象的相等取决于您的等式方法实现。
此外,如果您的equals方法比较仅在第一次保持对象时生成的ID,则会遇到麻烦。 他们可能不在那里,当平等被称为。