java – 在hibernate中将散列函数委托给未初始化的委托会导致更改hashCode

前端之家收集整理的这篇文章主要介绍了java – 在hibernate中将散列函数委托给未初始化的委托会导致更改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;
  }
}

请注意,hashCode()的实现完全遵循in the hibernate documentation给出的建议.

现在,如果我加载一个Compound类型的对象,它会急切地加载带有部件的HasSet.这会调用部件上的hashCode(),然后调用复合体上的hashCode().但问题是,此时并非所有考虑用于创建复合的hashCode的值都可用.因此,部件的hashCode在初始化完成后改变,从而制动HashSet的合同并导致各种难以跟踪的错误(例如,在部件组中设置两次相同的对象).

所以我的问题是:避免这个问题的最简单的解决方案是什么(我想避免为自定义加载/初始化编写类)?我完全做错了吗?

编辑:我错过了什么吗?这似乎是一个基本问题,为什么我在任何地方都找不到任何关于它的东西?

Instead of using the database identifier for the equality comparison,
you should use a set of properties for equals() that identify your
individual objects. […] No need to use the persistent identifier,the so
called “business key” is much better. It’s a natural key,but this
time there is nothing wrong in using it! (07001)

It is recommended that you implement equals() and hashCode() using
Business key equality. Business key equality means that the equals()
method compares only the properties that form the business key. It is
a key that would identify our instance in the real world (a natural
candidate key). (07002)

编辑:这是加载发生时的堆栈跟踪(如果这有帮助).在那个时间点,属性someUniqueName为null,因此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,boolean) line: 993  
EntityLoader(Loader).doQuery(SessionImplementor,QueryParameters,boolean) line: 857    
EntityLoader(Loader).doQueryAndInitializeNonLazyCollections(SessionImplementor,boolean) line: 274 
EntityLoader(Loader).loadEntity(SessionImplementor,Type,String,Serializable,EntityPersister,LockOptions) line: 2037    
EntityLoader(AbstractEntityLoader).load(SessionImplementor,LockOptions) line: 86 
EntityLoader(AbstractEntityLoader).load(Serializable,LockOptions) line: 76 
SingleTableEntityPersister(AbstractEntityPersister).load(Serializable,LockOptions,SessionImplementor) line: 3293  
DefaultLoadEventListener.loadFromDatasource(LoadEvent,EntityKey,LoadEventListener$LoadType) line: 496    
DefaultLoadEventListener.doLoad(LoadEvent,LoadEventListener$LoadType) line: 477    
DefaultLoadEventListener.load(LoadEvent,LoadEventListener$LoadType) line: 227  
DefaultLoadEventListener.proxyOrLoad(LoadEvent,LoadEventListener$LoadType) line: 269   
DefaultLoadEventListener.onLoad(LoadEvent,LoadEventListener$LoadType) line: 152    
SessionImpl.fireLoad(LoadEvent,LoadEventListener$LoadType) line: 1090  
SessionImpl.internalLoad(String,boolean,boolean) line: 1038 
ManyToOneType(EntityType).resolveIdentifier(Serializable,SessionImplementor) line: 630 
ManyToOneType(EntityType).resolve(Object,Object) line: 438 
TwoPhaseLoad.initializeEntity(Object,PreLoadEvent,PostLoadEvent) line: 139   
QueryLoader(Loader).initializeEntitiesAndCollections(List,boolean) line: 982   
QueryLoader(Loader).doQuery(SessionImplementor,boolean) line: 857 
QueryLoader(Loader).doQueryAndInitializeNonLazyCollections(SessionImplementor,boolean) line: 274  
QueryLoader(Loader).doList(SessionImplementor,QueryParameters) line: 2542  
QueryLoader(Loader).listIgnoreQueryCache(SessionImplementor,QueryParameters) line: 2276    
QueryLoader(Loader).list(SessionImplementor,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>

解决方法

你有一个完美的合法用例,事实上它应该有效.但是,如果在设置’someUniqueName’之前设置Compound对象的’parts’,那么在常规Java中会遇到同样的问题.

因此,如果你能说服hibernate在’parts’属性之前设置’someUniqueName’属性.您是否尝试过在java类中重新排序它们?或者将“部分”重命名为“zparts”? hibernate文档只是说订单不能保证.我在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;
  }
}

在Compound.equals()中,确保它也以

public boolean equals(Object o)
{
    if (this == o) return true;

这应该避免你现在遇到的问题.

hashCode()方法中的每个属性都应该在equals()方法中,但不一定是相反的方式.

原文链接:https://www.f2er.com/java/239977.html

猜你在找的Java相关文章