Explain Codes LogoExplain Codes Logo

How to fetch FetchType.LAZY associations with JPA and Hibernate in a Spring Controller

java
lazy-loading
jpa
hibernate
Nikita BarsukovbyNikita Barsukov·Sep 23, 2024
TLDR

To manage Hibernate sessions for LAZY associations, use the decorator @Transactional. Have a look at this code snippet:

@Transactional public SomeEntity fetchWithLAZY(Long id) { SomeEntity entity = repo.getById(id); // Like finding a lost friend in a crowd Hibernate.initialize(entity.getLAZYAssn()); // 'Awake, my sleeping beauty...!' return entity; }

With this pattern, LAZY associations are initialized within a Hibernate session that's up and running - thus, you can bid a not-so-tearful adieu to the feared LazyInitializationException. All you need to do is call Hibernate.initialize() on each LAZY association you want to be loaded.

Making sense of FetchType.LAZY

Efficiently handling LAZY loading in a Spring Controller requires a number of tactics and considerations:

The magic of fetch joins

Using Fetch joins in JPQL help you to load associated entities in one go instead of loading them separately. This can be accomplished directly in a repository method:

@Repository public interface SomeEntityRepository extends JpaRepository<SomeEntity, Long> { @Query("SELECT e FROM SomeEntity e JOIN FETCH e.lazyRelation WHERE e.id = :id") SomeEntity findByIdAndFetchLazyRelationEagerly(@Param("id") Long id); // It's fetching time! }

A service layer, your session guardian

An intermediate Service layer can work like a buffer between your controller and your repositories. It makes sure the session is up and running while operations on LAZY collections are carried out.

Use @EntityGraph for a custom touch

You can customize fetching strategies through @EntityGraph. This allows for the specification of a load graph at the method level in your repository:

@EntityGraph(attributePaths = {"lazyRelation"}) Optional<SomeEntity> findById(Long id); // 'When life gives you lazy relations, make eager loading!'

Single fetch join VS multiple queries

It's critical to think about the trade-off between multiple queries and using fetch joins. Fetch joins might decrease the number of queries but, beware, they also might lead to fetching more data than actually required.

@Transactional: non-negotiable!

Ensure that your fetching protocol always operates within a @Transactional framework so that the session remains open and the dreaded LazyInitializationException does not pop up.

Get those proxies working

Maximize the usage capabilities of Spring Data JPA's findById or custom methods for fetching LAZY associations in a proper manner. Also, make sure to use proxies to tie parent entities when persisting child entities to bypass any lazy loading-related complications.

Performance matters

Concern yourself with loading LAZY data only as required in order to keep off unnecessary overhead. Eagerly loading all relations, irrespective of actual need, can be detrimental to performance.

A deep dive into FetchType.LAZY

Life-cycle management of session

In order for LAZY initialization to succeed, a Hibernate session needs to be up and running. The OpenEntityManagerInViewFilter can help keep the session open for the whole duration of a web request, thereby helping you evade the LazyInitializationException.

Initiation of lazy loading

Either a service method or a direct access to the lazy collection can kick-start lazy loading. If you call .size() on the collection or implement any other method that needs to gain access to the collection data, initialization is triggered.

Eager fetching: use with caution

Do exercise caution while setting fetching to FetchType.EAGER or using eager fetching repository methods indiscriminately, as doing so could lead to performance degradation and a ramp-up in memory consumption.

Repository helper utilities

Do give a thought to creating a utility such as a RepositoryHelper or utilizing JpaUtils to separate the often complicated entity initialization logic from your business service layer, thereby keeping your codebase clean and maintainable.

Avoid LazyInitializationException

Implement the following practices and strategies to silently load lazy associations, or gracefully tackle exceptions in scenarios where lazy data is being accessed out of transaction scope:

  1. Service Layer - Ensures transactions are open during lazy loading.
  2. Entity Graphs - Define fetch plans for selective eager loading.
  3. Spring Repositories - Manage how and when to fetch associations with custom methods.