应该如何equals和hashCode使用JPA时和Hibernate
应该如何模型类的equals和hashCode在Hibernate中实现实施?常见的缺陷是什么?大多数情况下,默认实现是否足够好?使用业务密钥有什么意义吗?应该如何equals和hashCode使用JPA时和Hibernate
在我看来,这是相当困难的得到它的权利,在任何情况下,当延迟抓取,ID生成,代理等都是考虑的工作。
具有休眠的时候/如何documentation
覆盖equals()
/hashCode()
一个漫长且愉快描述它的要点是你唯一需要担心它,如果你的实体将是一个Set
,或者如果你的一部分将要分离/附加它的实例。后者并不常见。前者通常是最好的处理通过:
- 对业务关键立足
equals()
/hashCode()
- 例如在对象(或至少会话)生命周期中不会更改的属性的唯一组合。 - 如果以上是不可能的,则在主键上设置基础
equals()
/hashCode()
如果设置了,则以及其他对象标识/System.identityHashCode()
。 重要这里的一部分是,你需要重新加载你的设置后新的实体已被添加到它并坚持;否则最终会出现奇怪的行为(最终导致错误和/或数据损坏),因为您的实体可能被分配到与当前的hashCode()
不匹配的存储桶。
当你说“重新加载”@ChssPly76你的意思是做一个'refresh()'?你的实体如何遵守'Set'合同,最终会出现在错误的桶中(假设你有足够好的哈希码实现)。 – 2009-10-28 19:21:26
刷新收藏或重新加载整个(所有者)实体,是的。至于错误的桶:a)你添加新的实体来设置,它的ID还没有设置,所以你使用的是identityHashCode,它将你的实体放在bucket#1中。 b)你的实体(在set中)是持久的,它现在有一个id,因此你使用基于该id的hashCode()。它与上面的不同之处在于**会将您的实体放置在第2个桶中。现在,假设你在其他地方持有对此实体的引用,请尝试调用'Set.contains(entity)'并返回'false'。 – ChssPly76 2009-10-28 19:33:57
有道理但从未使用过identityHashCode自己虽然我看到它在Hibernate源代码中使用,就像在他们的ResultTransformers中一样 – 2009-10-29 05:37:28
是的,这很难。在我的项目中,equals和hashCode都依赖于对象的id。这个解决方案的问题是,如果对象还没有被保存,它们都不会工作,因为id是由数据库生成的。在我的情况下,这是可以容忍的,因为在几乎所有情况下,对象都会立即被持久化。除此之外,它很好用,很容易实现。
我认为我们所做的就是在没有生成id的情况下使用对象标识 – 2009-10-28 17:22:40
问题这里是,如果你坚持对象,你的哈希码改变。如果对象已经是基于散列的数据结构的一部分,那么这可能会产生很大的不利结果。因此,如果你确实使用了对象标识,那么最好继续使用obj id,直到对象完全释放(或者从任何基于散列的结构中删除对象,然后将其添加回去)。就个人而言,我认为最好不要使用id,并将哈希基于对象的不可变属性。 – 2009-10-29 03:55:08
我不认为接受的答案是正确的。
要回答原来的问题:
是默认的实现大多数情况不够好?
答案是肯定的,它是大多数情况下。
你只需要重写equals()
和hashcode()
如果实体将在Set
使用(这是很常见)和实体将被分离,并随后重新附着,休眠会话(这是一个罕见的休眠使用)。
接受的答案表示如果或条件为真,则需要重写方法。
这与我的观察一致,有时间找出[为什么](http://docs.jboss.org/hibernate/core/4.0/manual/en-US/html/persistent -classes.html#持久类-equalshashcode)。 – 2016-02-11 16:31:24
“如果实体将在Set中使用,则只需要覆盖equals()和hashcode()就足够了,如果某些字段标识对象,并且您不想依赖Object.equals()识别对象。 – davidxxx 2017-11-11 21:23:58
当实体通过延迟加载加载时,它不是基类型的实例,而是由javassist生成的动态生成的子类型,因此对相同类类型的检查将失败,因此请勿使用:
if (getClass() != that.getClass()) return false;
改用:
if (!(otherObject instanceof Unit)) return false;
这也是一个很好的做法,因为在Implementing equals in Java Practices解释。
由于相同的原因,直接访问字段可能无法正常工作并返回null,而不是基础值,因此不要使用属性比较,而是使用getter,因为它们可能触发加载基础值。
什么解释,谢谢先生访问地球.. – 2015-02-25 08:18:56
如果你正在比较具体类的对象,这在我的情况不起作用,这工作。我正在比较超类的对象,在这种情况下,此代码适用于我:obj1.getClass()。isInstance(obj2) – Tad 2015-08-07 19:38:42
最好的equals
/hashCode
实施是当你使用unique business key。
商业密钥应该在所有entity state transitions(瞬态,附加,分离,删除)中保持一致,这就是为什么你不能依靠id来实现平等。
另一种选择是切换到使用由应用程序逻辑分配的UUID identifiers。这样,您可以使用UUID作为equals
/hashCode
,因为在实体被刷新之前分配了id。
您甚至可以使用equals
和hashCode
实体标识符,但需要你总是返回相同hashCode
值,以便您确保实体的hashCode值是所有实体状态转变相一致。检出this post for more on this topic。
+1用于uuid方法。把它放到'BaseEntity'中,不要再考虑这个问题。它需要在数据库方面的一点空间,但你更好的价格支付的安慰:) – 2016-08-25 13:49:55
从文章中引用的重要防线:
我们建议使用业务键值 平等实现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
}
}
如果你碰巧覆盖equals
,确保履行合同: -
- 对称性
- 反光
- 传递性
- 洽
- 非零
,并覆盖hashCode
,作为其合同依靠equals
实施。
Joshua Bloch(Collection框架的设计者)强烈要求遵循这些规则。
- 项目9:始终重写了hashCode当您覆写等于
有严重的时候,你不遵循这些合同意想不到的效果。例如,List.contains(Object o)
可能因为未履行一般合同而返回错误的boolean
价值。
在Hibernate 5.2的文档中,它表示你可能不想实现hashCode和等于根据你的情况。
通常,如果他们是在数据库中等于(不实施的hashCode和等于)来自相同会话加载两个对象将是相等的。
如果您使用两个或多个会话,则会变得复杂。在这种情况下,两个对象的相等取决于您的equals方法实现。
此外,如果您的equals方法比较仅在第一次保持对象时生成的ID,则会遇到麻烦。当等号被呼叫时,他们可能不在那里。
另请参见http://stackoverflow.com/a/39827962/548473(spring-data-jpa的实现) – GKislin 2016-10-03 09:07:25