У меня проблема с 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()
полностью следует рекомендациям, данным в документации по спящему режиму .
Теперь, если я загружаю объект типа Compound
, он охотно загружает HasSet
деталями.Это вызывает hashCode()
на деталях, что, в свою очередь, вызывает hashCode()
на соединении.Однако проблема заключается в том, что на этом этапе еще не все значения, которые рассматриваются для создания хэш-кода соединения, все еще доступны.Следовательно, хэш-код частей изменяется после завершения инициализации, что приводит к торможению контракта HashSet
и приводит к всевозможным ошибкам, которые трудно отследить (например, наличие одного и того же объекта вдетали устанавливаются дважды).
Итак, мой вопрос: каково самое простое решение, чтобы избежать этой проблемы (я хотел бы избежать написания классов для пользовательской загрузки / инициализации)?Я делаю что-то здесь не так?
Редактировать : Я что-то здесь упускаю?Это кажется основной проблемой, почему я нигде ничего об этом не нахожу?
Вместо использования идентификатора базы данных для сравнения на равенство, вы должны использовать набор свойств для equals ()которые идентифицируют ваши отдельные объекты.[...] Нет необходимости использовать постоянный идентификатор, так называемый «бизнес-ключ» намного лучше.Это естественный ключ, но на этот раз в его использовании нет ничего плохого!( статья из hibernate )
И
Рекомендуется реализовать equals () и hashCode () с использованием равенства бизнес-ключей.Равенство бизнес-ключей означает, что метод equals () сравнивает только те свойства, которые образуют бизнес-ключ.Это ключ, который идентифицирует наш экземпляр в реальном мире (естественный ключ-кандидат).( документация hibernate )
Редактировать: Это трассировка стека, когда происходит загрузка (если это помогает).В этот момент атрибут someUniqueName
является нулевым, и, следовательно, хэш-код вычисляется неправильно.
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>