After the introduction of JPA proxy compliance in 5.2.13 (HHH-12034) I have noticed that lazy loading of ManyToOne relatioships does not work anymore (target entity is eagerly loaded with the parent entity). I am using WildFly 14 (Hibernate 5.3.6) with L2 cache enabled (Infinispan). WildFly sets hibernate.jpa.compliance.proxy to true by default.
Consider the following scenarios:
- Entity A has a ManyToOne relationship with entity B (@ManyToOne(fetch = FetchType.LAZY)). Loading entity A will also load entity B.
- Entity C has a OneToMany relationship with the above entity A. When the collection in C is initialized it will load the corresponding A entities as expected but it will also load the corresponding B entities which is not expected.
In scenario 2 from above the load of B was triggered as follows:
javax.persistence.EntityNotFoundException: Unable to find <<entity B class>> with id 2789430001
at org.hibernate@5.3.6.Final//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate.handleEntityNotFound(EntityManagerFactoryBuilderImpl.java:162)
at org.hibernate@5.3.6.Final//org.hibernate.proxy.AbstractLazyInitializer.checkTargetState(AbstractLazyInitializer.java:285)
at org.hibernate@5.3.6.Final//org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:180)
at org.hibernate@5.3.6.Final//org.hibernate.proxy.AbstractLazyInitializer.getIdentifier(AbstractLazyInitializer.java:89)
at org.hibernate@5.3.6.Final//org.hibernate.internal.SessionImpl.getProxyIdentifier(SessionImpl.java:1752)
at org.hibernate@5.3.6.Final//org.hibernate.internal.SessionImpl.getContextEntityIdentifier(SessionImpl.java:1743)
at org.hibernate@5.3.6.Final//org.hibernate.engine.internal.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:271)
at org.hibernate@5.3.6.Final//org.hibernate.type.ManyToOneType.disassemble(ManyToOneType.java:270)
at org.hibernate@5.3.6.Final//org.hibernate.type.TypeHelper.disassemble(TypeHelper.java:132)
at org.hibernate@5.3.6.Final//org.hibernate.cache.spi.entry.StandardCacheEntryImpl.<init>(StandardCacheEntryImpl.java:53)
at org.hibernate@5.3.6.Final//org.hibernate.persister.entity.AbstractEntityPersister$StandardCacheEntryHelper.buildCacheEntry(AbstractEntityPersister.java:5503)
at org.hibernate@5.3.6.Final//org.hibernate.persister.entity.AbstractEntityPersister.buildCacheEntry(AbstractEntityPersister.java:4503)
at org.hibernate@5.3.6.Final//org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:203)
at org.hibernate@5.3.6.Final//org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:129)
at org.hibernate@5.3.6.Final//org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.performTwoPhaseLoad(AbstractRowReader.java:238)
at org.hibernate@5.3.6.Final//org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:209)
at org.hibernate@5.3.6.Final//org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:133)
at org.hibernate@5.3.6.Final//org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:122)
at org.hibernate@5.3.6.Final//org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:86)
at org.hibernate@5.3.6.Final//org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87)
at org.hibernate@5.3.6.Final//org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:691)
at org.hibernate@5.3.6.Final//org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:75)
at org.hibernate@5.3.6.Final//org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2246)
at org.hibernate@5.3.6.Final//org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:580)
at org.hibernate@5.3.6.Final//org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:262)
at org.hibernate@5.3.6.Final//org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:576)
at org.hibernate@5.3.6.Final//org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:147)
at org.hibernate@5.3.6.Final//org.hibernate.collection.internal.AbstractPersistentCollection$3.doWork(AbstractPersistentCollection.java:339)
at org.hibernate@5.3.6.Final//org.hibernate.collection.internal.AbstractPersistentCollection$3.doWork(AbstractPersistentCollection.java:327)
at org.hibernate@5.3.6.Final//org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:262)
at org.hibernate@5.3.6.Final//org.hibernate.collection.internal.AbstractPersistentCollection.readElementExistence(AbstractPersistentCollection.java:326)
at org.hibernate@5.3.6.Final//org.hibernate.collection.internal.PersistentSet.add(PersistentSet.java:208)
EntityNotFoundException is expected in my scenario as a concurrent transaction has deleted some entities A and B while the current transaction is initializing the collection. However while this exception is expected, it has revealed that Hibernate is trying to load a lazy loaded entity (B) although the application did not access it.
If I’m reading the above stacktrace correctly I would say that the identifier of B is accessed while A entity is dehydrated for storing in L2 cache.
Also loading of entity A by calling EntityManager.find() will always execute 2 database queries one for entity A and one for entity B when neither A nor B are in L2 cache.
Setting hibernate.jpa.compliance.proxy to false works as expected (B is not loaded when loading A).
So my question is whether this is the expected behavior as it basically means that lazy loading does not work when L2 cache is enabled?
Thanks,
Marius