JPA的hashCode()/ equals()两难
这里有一些关于JPA实体的讨论,以及哪些hashCode()
/ equals()
实现应该用于JPA实体类。 大多数(如果不是全部的话)都依赖于Hibernate,但我想讨论它们JPA-implementation-neutrally(顺便说一句,我使用EclipseLink)。
所有可能的实现都有自己的优点和缺点 :
hashCode()
/ equals()
)为List
/ Set
操作提供合同一致性 (不变性) 据我所知,有三种选择 :
Object.equals()
和Object.hashCode()
hashCode()
/ equals()
工作 hashCode()
/ equals()
被破坏 hashCode()
/ equals()
被破坏 我的问题是:
更新1:
通过“ hashCode()
/ equals()
被破坏”,我的意思是连续的hashCode()
调用可能会返回不同的值,这是(正确实现时)不会在Object
API文档意义上破坏,但是在尝试时会导致问题从Map
, Set
或其他基于散列的Collection
检索已更改的实体。 因此,JPA实现(至少EclipseLink)在某些情况下无法正常工作。
更新2:
感谢您的答复 - 其中大部分都具有卓越的品质。
不幸的是,我仍然不确定哪种方法对于实际应用程序是最好的,或者如何确定我的应用程序的最佳方法。 所以,我会保持这个问题的公开并希望有更多的讨论和/或意见。
阅读这篇关于这个主题的非常好的文章:不要让Hibernate窃取你的身份。
文章的结论如下:
当对象持久化到数据库时,对象标识很难正确实现。 但是,这些问题完全来自于允许对象在保存前没有ID而存在。 我们可以通过从对象关系映射框架(如Hibernate)中分配对象ID来解决这些问题。 相反,一旦对象被实例化,对象ID就可以被分配。 这使对象标识简单且无错,并减少了域模型中所需的代码量。
我总是覆盖equals / hashcode并根据业务ID实现它。 对我来说似乎是最合理的解决方案。 请参阅以下链接。
为了总结所有这些东西,下面列出了使用不同方法处理equals / hashCode的工作或不工作:
编辑 :
为了解释为什么这对我有效:
我们通常在我们的实体中有两个ID:
equals()
和hashCode()
) 看一看:
@Entity
public class User {
@Id
private int id; // Persistence ID
private UUID uuid; // Business ID
// assuming all fields are subject to change
// If we forbid users change their email or screenName we can use these
// fields for business ID instead, but generally that's not the case
private String screenName;
private String email;
// I don't put UUID generation in constructor for performance reasons.
// I call setUuid() when I create a new entity
public User() {
}
// This method is only called when a brand new entity is added to
// persistence context - I add it as a safety net only but it might work
// for you. In some cases (say, when I add this entity to some set before
// calling em.persist()) setting a UUID might be too late. If I get a log
// output it means that I forgot to call setUuid() somewhere.
@PrePersist
public void ensureUuid() {
if (getUuid() == null) {
log.warn(format("User's UUID wasn't set on time. "
+ "uuid: %s, name: %s, email: %s",
getUuid(), getScreenName(), getEmail()));
setUuid(UUID.randomUUID());
}
}
// equals() and hashCode() rely on non-changing data only. Thus we
// guarantee that no matter how field values are changed we won't
// lose our entity in hash-based Sets.
@Override
public int hashCode() {
return getUuid().hashCode();
}
// Note that I don't use direct field access inside my entity classes and
// call getters instead. That's because Persistence provider (PP) might
// want to load entity data lazily. And I don't use
// this.getClass() == other.getClass()
// for the same reason. In order to support laziness PP might need to wrap
// my entity object in some kind of proxy, i.e. subclassing it.
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (!(obj instanceof User))
return false;
return getUuid().equals(((User) obj).getUuid());
}
// Getters and setters follow
}
编辑:澄清我有关调用setUuid()
方法的setUuid()
。 这是一个典型的场景:
User user = new User();
// user.setUuid(UUID.randomUUID()); // I should have called it here
user.setName("Master Yoda");
user.setEmail("yoda@jedicouncil.org");
jediSet.add(user); // here's bug - we forgot to set UUID and
//we won't find Yoda in Jedi set
em.persist(user); // ensureUuid() was called and printed the log for me.
jediCouncilSet.add(user); // Ok, we got a UUID now
当我运行测试并查看日志输出时,我解决了这个问题:
User user = new User();
user.setUuid(UUID.randomUUID());
或者,可以提供一个单独的构造函数:
@Entity
public class User {
@Id
private int id; // Persistence ID
private UUID uuid; // Business ID
... // fields
// Constructor for Persistence provider to use
public User() {
}
// Constructor I use when creating new entities
public User(UUID uuid) {
setUuid(uuid);
}
... // rest of the entity.
}
所以我的例子看起来像这样:
User user = new User(UUID.randomUUID());
...
jediSet.add(user); // no bug this time
em.persist(user); // and no log output
我使用默认的构造函数和setter,但是您可能会发现两个构造函数更适合您。
链接地址: http://www.djcxy.com/p/36979.html上一篇: The JPA hashCode() / equals() dilemma
下一篇: Order By