如何处理由hibernate管理的java对象的对象相等性?在“hibernate in action”一书中,他们说人们应该偏爱商业密钥而不是代理密钥 大多数时候,我没有业务密钥。想一想映射到一个人的地址。地址保存在Set中并显示在Wicket RefreshingView中(使用ReuseIfEquals策略)。
我可以使用代理id或使用equals()和hashCode()函数中的所有字段。
问题是这些字段在生命周期内对象变化。要么是因为用户输入了一些数据,要么由于在OSIV(在视图中打开会话)过滤器内调用JPA merge()而导致id发生变化。
我对equals()和hashCode()契约的理解是那些在对象的生命周期内不应该改变。
到目前为止我尝试了什么:
我需要的是在我认为在对象创建期间分配的ID。这里有什么选择?我不想引入一些额外的持久属性。有没有办法明确告诉JPA为对象分配ID?
此致
答案 0 :(得分:14)
使用实体的id
不是一个好主意,因为瞬态实体还没有id(并且你仍然希望瞬态实体可能等同于持久实体)。
使用所有属性(除了数据库标识符)也不是一个好主意,因为所有属性都不是标识的一部分。
因此,实现相等性的首选(和正确)方法是使用业务键,如 Java Persistence with Hibernate 中所述:
使用业务键实现相等性
要获得我们推荐的解决方案,您需要了解其概念 一个商业密钥。业务键是属性或属性的某种组合 对于具有相同数据库标识的每个实例都是唯一的。从本质上讲,如果您没有使用代理主键,那么它将是您使用的自然键。 与自然主键不同,它不是业务的绝对要求 密钥永远不会改变 - 只要它很少变化,这就足够了。
我们认为基本上每个实体类都应该有一些业务密钥,甚至 如果它包括该类的所有属性(这适用于某些人 不可变的类)。业务密钥是用户认为唯一标识特定记录的内容,而代理密钥是应用程序和 数据库使用。
业务键相等意味着equals()方法仅比较构成业务键的属性。这是一个完美的解决方案,可以避免前面描述的所有问题。唯一的缺点是需要额外考虑 首先确定正确的业务密钥。无论如何都需要这种努力; 如果数据库必须通过约束检查确保数据完整性,则识别任何唯一键很重要。
对于User类,
username
是一个很好的候选业务键。它永远不会为空 它与数据库约束是唯一的,如果有的话,它很少变化:public class User { ... public boolean equals(Object other) { if (this==other) return true; if ( !(other instanceof User) ) return false; final User that = (User) other; return this.username.equals( that.getUsername() ); } public int hashCode() { return username.hashCode(); } }
也许我错过了一些东西,但是对于地址,商业密钥通常由街道号码,街道,城市,邮政编码,国家组成。我没有看到任何问题。
以防万一,Equals And HashCode是另一个有趣的读物。
答案 1 :(得分:0)
也许transient
属性会这样做?
这样你就不必担心持久性了。像这样:
@Transient
private Integer otherId;
答案 2 :(得分:0)
我习惯这样做:equal和hashcode在设置时使用密钥,否则equals使用基本实现(aka ==)。如果hashcode()
返回super.hashcode()
而不是0,它也应该有用。
@Override
public int hashCode() {
if (code == null) {
return 0;
} else {
return code.hashCode();
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PersistentObject && Hibernate.getClass(obj).equals(Hibernate.getClass(this))) {
PersistentObject po = (PersistentObject) obj;
if (code == null) {
return po.code == null && this == po;
} else {
return code.equals(po.getCode());
}
} else {
return super.equals(obj);
}
}
答案 3 :(得分:0)
问题是,您有多少次可能有多个未保存的对象可能是需要进入集合或地图的重复项?对我来说,答案几乎从来没有,所以我对未保存的对象使用代理键和super.equals / hashcode。
在某些情况下,业务键很有意义,但它们可能会导致问题。例如,如果两个人住在同一个地址怎么办 - 如果你想让它成为数据库中的一个记录,那么你必须将它作为多对多管理并失去级联删除它的能力所以当最后一个生活在那里的人被删除,你必须做额外的工作来摆脱地址。但是,如果您为每个人存储相同的地址,那么您的业务密钥必须包含person实体,这可能意味着您的equals / hashcode方法中存在数据库。
答案 4 :(得分:0)
感谢您的所有投入。我决定使用代理键并在对象创建时提供这些代码。通过这种方式,我可以清除所有'很少'改变的东西,并拥有坚实的基础身份。第一次测试看起来相当不错。
谢谢大家的时间。不幸的是,我只能接受一个答案作为解决方案,我将采取帕斯卡,因为他给了我很好的阅读;)
享受