@ManyTo one related entity causes LazyInstantiationException in Transaction

#1

Okay, this one puzzles me. I have added a hashCode method to my entity that hashes the owning entity from a @ManyToOne relationship too. (it is kind of a crystallized m:n relationship with properties ). As the owning entity has a correlating @OneToMany set property, entities in the set need to have the hashCode and equals methods.

What I now found is that a Spring service method that is marked @Transactional pulls an entity from the entity manager and then ask for its lazy loaded set of a related entity that has ‘@ManyToOne’ marked properties used in its hashCode function (one of which is the entity that owns the set), then Hibernate throws an exception. Here’s the anonymized Stacktrace:

	at org.hibernate.bytecode.enhance.spi.interceptor.Helper.throwLazyInitializationException(Helper.java:164) 
	at org.hibernate.bytecode.enhance.spi.interceptor.Helper.performWork(Helper.java:59) 
	at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.loadAttribute(LazyAttributeLoadingInterceptor.java:76) 
	at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.fetchAttribute(LazyAttributeLoadingInterceptor.java:72) 
	at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.intercept(LazyAttributeLoadingInterceptor.java:61) 
	at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.readObject(LazyAttributeLoadingInterceptor.java:296) 
	at ourcorp.app.model.Owned.$$_hibernate_read_version(Owned.java) 
	at ourcorp.app.model.Owned.hashCode(Owned.java:312) 
	at java.util.HashMap.hash(HashMap.java:339) ~[?:1.8.0_192]
	at java.util.HashMap.put(HashMap.java:612) ~[?:1.8.0_192]
	at java.util.HashSet.add(HashSet.java:220) ~[?:1.8.0_192]
	at java.util.AbstractCollection.addAll(AbstractCollection.java:344) ~[?:1.8.0_192]
	at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:355) 
	at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:236) 
	at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:223) 
	at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:196) 
	at org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl.endLoading(CollectionReferenceInitializerImpl.java:154) 
	at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishLoadingCollections(AbstractRowReader.java:252) 
	at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:212) 
	at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:133) 
	at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:122) 
	at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:86) 
	at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) 
	at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:691) 
	at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:75) 
	at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2259) 
	at org.hibernate.persister.entity.AbstractEntityPersister.initializeLazyProperty(AbstractEntityPersister.java:1065) 
	at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor$1.doWork(LazyAttributeLoadingInterceptor.java:105) 
	at org.hibernate.bytecode.enhance.spi.interceptor.Helper.performWork(Helper.java:97) 
	at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.loadAttribute(LazyAttributeLoadingInterceptor.java:76) 
	at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.fetchAttribute(LazyAttributeLoadingInterceptor.java:72) 
	at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.intercept(LazyAttributeLoadingInterceptor.java:61) 
	at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.readObject(LazyAttributeLoadingInterceptor.java:296) 
	at ourcorp.app.model.Owner.$$_hibernate_read_owned(Learner.java) 
	at ourcorp.app.model.Owner.getOwnedOnes(Learner.java:383) 
	at ourcorp.app.Service.checkForDuplicates(Service.java:115) 

Maybe I do something wrong in my hashCode method?

0 Likes

#2

It’s impossible to tell if you don’t add the entity class to the post.

What I now found is that a Spring service method that is marked @Transactional pulls an entity from the entity manager and then ask for its lazy loaded set of a related entity that has ‘@ManyToOne’ marked properties used in its hashCode function (one of which is the entity that owns the set), then Hibernate throws an exception.

A code snippet is worth 1000 words. You need to add the data access code.

0 Likes

#3

I deleted that code as it failed…

from memory it was basically

@Entity
class A {

@OneToMany
Set<B> bs;

}

@Entity
class B {
@ManyToOne(fetch=LAZY)
A owner;

@ManyToOne(fetch=LAZY)
C related;

public boolean equals(Object o){
  if(o instanceof B){
    B other = (B) o;
    return Objects.equals(this.a, other.a) && Objects.equals(this.c, other.c)
   }
   return false;
  }

  public int hashCode(){
    return Objects.hash(a,c);
  }
}

@Entity
class C {
@OneToMany
Set<B> bs;
}

plus some business logic of course.

the query loads A and eager loads the Set of B. When feeding the HashSet, the hash calculation requires lazy loaded entities, but the session manager at that moment thinks it is not in a session.

0 Likes

#4

Checking the associations in equals is a bad idea.

This article explains the best way to write equals and hashCode for JPA entities.

1 Like