roe*_*erj 6 java hibernate initialization hashcode
我有hashCode()使用hibernate委托给未初始化对象的问题.
我的数据模型看起来如下(以下代码经过高度修剪以强调问题因此破坏,不要复制!):
class Compound {
@FetchType.EAGER
Set<Part> parts = new HashSet<Part>();
String someUniqueName;
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getSomeUniqueName() == null) ? 0 : getSomeUniqueName().hashCode());
return result;
}
}
class Part {
Compound compound;
String someUniqueName;
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getCompound() == null) ? 0 : getCompound().hashCode());
result = prime * result + ((getSomeUniqueName() == null) ? 0 : getSomeUniqueName().hashCode());
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,执行hashCode()完全遵循hibernate文档中给出的建议.
现在,如果我加载一个类型的对象Compound,它会急切地加载HasSet部件.这会调用hashCode()部件,然后调用hashCode()复合物.但问题是,此时并非所有考虑用于创建复合的hashCode的值都可用.因此,部件的hashCode 在初始化完成后改变,从而制动合同HashSet并导致各种难以跟踪的错误(例如,在部件设置中具有相同的对象两次).
所以我的问题是:避免这个问题的最简单的解决方案是什么(我想避免为自定义加载/初始化编写类)?我完全做错了吗?
编辑:我错过了什么吗?这似乎是一个基本问题,为什么我在任何地方都找不到任何关于它的东西?
您应该使用一组用于标识各个对象的equals()属性,而不是使用数据库标识符进行相等性比较.[...]无需使用持久性标识符,所谓的"业务密钥"要好得多.这是一个自然的关键,但这次使用它没有错!(来自hibernate的文章)
和
建议您使用Business key equality实现equals()和hashCode().业务键等式意味着equals()方法仅比较形成业务键的属性.它是识别我们在现实世界中的实例的关键(自然候选键).(hibernate文档)
编辑:这是加载发生时的堆栈跟踪(如果这有帮助).在那个时间点,该属性someUniqueName为空,因此错误地计算了hashCode.
Compound.getSomeUniqueName() line: 263
Compound.hashCode() line: 286
Part.hashCode() line: 123
HashMap<K,V>.put(K, V) line: 372
HashSet<E>.add(E) line: 200
HashSet<E>(AbstractCollection<E>).addAll(Collection<? extends E>) line: 305
PersistentSet.endRead() line: 352
CollectionLoadContext.endLoadingCollection(LoadingCollectionEntry, CollectionPersister) line: 261
CollectionLoadContext.endLoadingCollections(CollectionPersister, List) line: 246
CollectionLoadContext.endLoadingCollections(CollectionPersister) line: 219
EntityLoader(Loader).endCollectionLoad(Object, SessionImplementor, CollectionPersister) line: 1005
EntityLoader(Loader).initializeEntitiesAndCollections(List, Object, SessionImplementor, boolean) line: 993
EntityLoader(Loader).doQuery(SessionImplementor, QueryParameters, boolean) line: 857
EntityLoader(Loader).doQueryAndInitializeNonLazyCollections(SessionImplementor, QueryParameters, boolean) line: 274
EntityLoader(Loader).loadEntity(SessionImplementor, Object, Type, Object, String, Serializable, EntityPersister, LockOptions) line: 2037
EntityLoader(AbstractEntityLoader).load(SessionImplementor, Object, Object, Serializable, LockOptions) line: 86
EntityLoader(AbstractEntityLoader).load(Serializable, Object, SessionImplementor, LockOptions) line: 76
SingleTableEntityPersister(AbstractEntityPersister).load(Serializable, Object, LockOptions, SessionImplementor) line: 3293
DefaultLoadEventListener.loadFromDatasource(LoadEvent, EntityPersister, EntityKey, LoadEventListener$LoadType) line: 496
DefaultLoadEventListener.doLoad(LoadEvent, EntityPersister, EntityKey, LoadEventListener$LoadType) line: 477
DefaultLoadEventListener.load(LoadEvent, EntityPersister, EntityKey, LoadEventListener$LoadType) line: 227
DefaultLoadEventListener.proxyOrLoad(LoadEvent, EntityPersister, EntityKey, LoadEventListener$LoadType) line: 269
DefaultLoadEventListener.onLoad(LoadEvent, LoadEventListener$LoadType) line: 152
SessionImpl.fireLoad(LoadEvent, LoadEventListener$LoadType) line: 1090
SessionImpl.internalLoad(String, Serializable, boolean, boolean) line: 1038
ManyToOneType(EntityType).resolveIdentifier(Serializable, SessionImplementor) line: 630
ManyToOneType(EntityType).resolve(Object, SessionImplementor, Object) line: 438
TwoPhaseLoad.initializeEntity(Object, boolean, SessionImplementor, PreLoadEvent, PostLoadEvent) line: 139
QueryLoader(Loader).initializeEntitiesAndCollections(List, Object, SessionImplementor, boolean) line: 982
QueryLoader(Loader).doQuery(SessionImplementor, QueryParameters, boolean) line: 857
QueryLoader(Loader).doQueryAndInitializeNonLazyCollections(SessionImplementor, QueryParameters, boolean) line: 274
QueryLoader(Loader).doList(SessionImplementor, QueryParameters) line: 2542
QueryLoader(Loader).listIgnoreQueryCache(SessionImplementor, QueryParameters) line: 2276
QueryLoader(Loader).list(SessionImplementor, QueryParameters, Set, Type[]) line: 2271
QueryLoader.list(SessionImplementor, QueryParameters) line: 459
QueryTranslatorImpl.list(SessionImplementor, QueryParameters) line: 365
HQLQueryPlan.performList(QueryParameters, SessionImplementor) line: 196
SessionImpl.list(String, QueryParameters) line: 1268
QueryImpl.list() line: 102
<my code where the query is executed>
Run Code Online (Sandbox Code Playgroud)
您有一个完美的合法用例,而且它确实应该有效。然而,如果您在设置“someUniqueName”之前设置复合对象的“部分”,那么在常规 Java 中您也会遇到同样的问题。
因此,如果您可以说服 hibernate 在“parts”属性之前设置“someUniqueName”属性。您是否尝试过在 java 类中对它们进行重新排序?或者将“parts”重命名为“zparts”?hibernate 文档只是说不能保证顺序。我会在休眠中提交一个错误以允许强制执行此命令......
另一个可能更容易的解决方案:
class Part {
public int hashCode() {
//don't include getCompound().hashCode()
return getSomeUniqueName() == null ? 0 : getSomeUniqueName().hashCode();
}
public boolean equals(Object o)
{
if (this == o) return true;
if (!o instanceof Part) return false;
Part part = (Part) o;
if (getCompound() != null ? !getCompound().equals(part.getCompound()) : part.getCompound()!= null)
return false;
if (getSomeUniqueName()!= null ? !getSomeUniqueName().equals(part.getSomeUniqueName()) : part.getSomeUniqueName()!= null)
return false;
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
在Compound.equals()中确保它也以
public boolean equals(Object o)
{
if (this == o) return true;
Run Code Online (Sandbox Code Playgroud)
这应该可以避免您现在遇到的问题。
hashCode() 方法中的每个属性都应该位于 equals() 方法中,但不一定是相反。
| 归档时间: |
|
| 查看次数: |
571 次 |
| 最近记录: |