Learn how to create flexible schemas in a relational database using SQL for JSON.

The short answer is: go Lazy if you care about number of queries happening under the hood. “But what if I always need a particular relation?” — still go lazy. Here is why.

There Is No Such Thing As a Free Lunch

I used to think: “In many scenarios, eager is for free. If in most queries you need a particular relation, then it’s better to fetch it eagerly in one join, and in those rare cases when don’t need it — one additional join costs almost nothing.”

select worker0_.personal_id as personal1_1_0_, worker0_.surname as surname2_1_0_, worker0_.unit_id as unit_id3_1_0_, unit1_.id as id1_0_1_ from worker worker0_ left outer join unit unit1_ on worker0_.unit_id=unit1_.id where worker0_.personal_id=?

This is perfect. I got only one query (join on worker and unit tables) and I can access all Unit information for free — without a need for an additional query. It’s true — but only when querying by id.

The Problem With Eager Mapping

select worker0_.personal_id as personal1_1_0_, worker0_.surname as surname2_1_0_, worker0_.unit_id as unit_id3_1_0_ from worker worker0_ where worker0_.personal_id=?
select unit0_.id as id1_0_0_ from unit unit0_ where unit0_.id=?

It happens because, with findBySurname(), you are asking only for the Worker entity. Then — when the object is constructed — the framework finds out that Worker.unit has an eager mapping — so it needs to be set, but because Unit data is not present. one more query needs to be performed.

Avoiding the Danger

So if you add an eager mapping — every time anyone adds new query method to your repository and forget to fetch all eager relations, an additional query will be created for every eager mapping in your entity!

And from the other side: For every query method in your repo, you will always need to manually fetch all eager relations — even when you don’t need them.

Imagine having a repo with 10 query methods — all fetching all eager relations. Now you need to modify your entity. If you add a new eager relation, you will need to add it to every existing query method to avoid additional joins. It doesn’t end with one repository. Imagine Unit having an eager relation on its own. Now you would need to add another fetch:

Of course, you can use entity graphs. These can be reused when being created in entity classes. But then you need to remember to add references over repository methods, and they also pollute entity classes. Moreover, they would need to repeat data in case when having an eager relation in an eager relation. (The EntityGraph for Unit needs to have information to eagerly fetch Owner, and the entity Graph for Worker needs to have information to eagerly fetch Unit and again Owner.)

The better solution would be to set all your relations as LAZY.

Conclusion

This model is lazy, but you can still have eager queries — by JPQL. You still need to manually fetch your needed relations (not doing so will create, as before, additional queries when in transaction or LazyIntializationException when not). But when you don’t need to access relation data, you are not forced to do any fetching and no extra query is performed. Moreover, adding a new Lazy relation shouldn’t affect exisiting code.

To sum it up: I’m not saying that everything should be lazy-loaded. The conclusion is: all entity mappings should be lazy, whereas eager loading should be performed by explicit fetching.