How to make lazy loading truly lazy (V5.4)


#1

Hi,

Since the begin of my project I notice difficulties with my ER model that hibernate is querying ToOne relationships even if I explicity mark them lazy. Here is a bit of code how it is currently done:

	@ManyToOne(fetch = LAZY, optional = false)
	@LazyToOne(NO_PROXY)
	@JoinColumn(name = "audit_tran_id")
	public AuditTransaction getAuditTransaction() {
		return auditTransaction;
	}

the classes are bytecode-enhanced by the maven plugin:

            <plugin>
                <groupId>org.hibernate.orm.tooling</groupId>
                <artifactId>hibernate-enhance-maven-plugin</artifactId>
                <version>${hibernate.version}</version>
                <executions>
                    <execution>
                        <configuration>
                            <enableLazyInitialization>true</enableLazyInitialization>
                        </configuration>
                        <goals>
                            <goal>enhance</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

and when my SpringBoot app is started it logs that, too
HHH000157: Lazy property fetching available for: [...]

Still, I notice unnecessary queries during the JPA load, in this case because the reference was broken, I got a nice stack trace:

javax.persistence.EntityNotFoundException: Unable to find mypackage.AuditTransaction with id 3133020786
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate.handleEntityNotFound(EntityManagerFactoryBuilderImpl.java:162) 
	at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:211) 
	at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:246) 
	at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:105) 
	at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:73) 
	at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1260) 
	at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1143) 
	at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:687) 
	at org.hibernate.type.EntityType.resolve(EntityType.java:464) 
	at org.hibernate.type.ManyToOneType.resolve(ManyToOneType.java:239) 
	at org.hibernate.type.EntityType.resolve(EntityType.java:457) 
	at org.hibernate.type.EntityType.nullSafeGet(EntityType.java:272) 
	at org.hibernate.persister.entity.AbstractEntityPersister.initializeLazyPropertiesFromDatastore(AbstractEntityPersister.java:1210) 
	at org.hibernate.persister.entity.AbstractEntityPersister.initializeLazyProperty(AbstractEntityPersister.java:1121) 
	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 mypackage.Achieved.$$_hibernate_read_version(Achieved.java) ~[classes!/:1-SNAPSHOT]
	at mypackage.Achieved.getVersion(Achieved.java:272) ~[classes!/:1-SNAPSHOT]
	at mypackage.Service.lambda$checkForDuplicateAchievement$2(Service.java:97) ~[classes!/:1-SNAPSHOT]

That brings up my question, did I oversee anything to get this a true lazy relation (because the audit information is only used when inserting),

And secondly, is there a JPA way to handle the situation gently when there is a referenced entity missing?


#2

The @ManyToOne associations don’t need bytecode enhancement to be loaded lazily.

Only the parent-side @OneToOne associations need that.


#3

Yes, that was my knowledge, too, but for reasons I do not fully comprehend, Hibernate now seems to see the difference between lazy and eager to be loaded with a join immediately or with a secondary query, before returning from the entity manager query. Nothing of my code uses the getter function, and I’d expect no query to be executed before my code needs it. But from the stack trace it is clear that Hibernate does so.
Why?


#4

Hibernate now seems to see the difference between lazy and eager to be loaded with a join immediately or with a secondary query, before returning from the entity manager query.

Nope. THat’s EAGER fetching. Lazy means that the association will not be fetched unless you navigate it explicitly.

Why?

The only way to figure it out is if you write a replicating test case that demonstrates the issue.


#5

EAGER: -> left join
LAZY -> query on demand.
At least from the documentation, but if it would be that easy, I would not have spent two weeks in December learning all about the maven-plugin to enhance the classes, to get things less database intense. And I still see those rogue queries for things not required by my logic. I cannot share most of my current code because it is closed source, but trust me, Hibernate is querying the entities related by ManyToOne even if I set the annotation to be lazy, even if I tell it to use the NO_PROXY approach and enhance the classes.
The problem could be the same as in https://hibernate.atlassian.net/browse/HHH-13134?jql=project%20%3D%20HHH%20AND%20component%20%3D%20bytecode-enhancement
But I started all that enhancing just because of the unwanted queries of manytoone relationships.
Is there a way to produce useful log files explaining why some lazy relations are loaded before anything is returned from the entity manager?


#6

And I still see those rogue queries for things not required by my logic.

If you are using LAZY and Hibernate fetches associations eagerly even if you use BYtecode Enhancement, then it’s a regression.

Since you already mentioned HHH-13134, the behavior might be caused by that issue.

I noticed there’s a test case provided for that issue too. Maybe Luis Barreiro will take a look at it. I pinged him on Jira since he knows the Bytecode Enhancement part better than anyone else in the team.