How to lazy load your Entity relations ?

This tutorial will teach you how to configure and optimize the fetch type strategy used in your Jakarta EE / Hibernate applications. In JPA terms, the FetchType strategy defines strategies for fetching data from the database.

The default FetchType depends on the cardinality of the relationship.

Here is a quick summary of defaults:

Fetch StrategyDefault strategy
OneToOneEAGER
ManyToOneEAGER
OneToManyLAZY
ManyToManyLAZY

For example, consider the following two Entity:

@Entity
public class Order {
    // ...
    
    @OneToMany(mappedBy = "order")
    private List<OrderItem> items;
    
    // Getter and setter
}

@Entity
public class OrderItem {
    // ...
    
    @ManyToOne
    private Order order;
    
    // Getter and setter
}

In this example, the Order entity has a one-to-many association with OrderItem entities. By default, the association is lazily loaded, so the items list will be loaded from the database only when accessed.

Specifying the Fetch Strategy with Annotations

You can specify a non-default fetching strategy using annotations. For example, here is how to use the EAGER Fetch Strategy through annotations:

@Entity
public class Order {
    // ...
    
    @OneToMany(mappedBy = "order", fetch = FetchType.EAGER)
    private List<OrderItem> items;
    
    // Getter and setter
}

@Entity
public class OrderItem {
    // ...
    
    @ManyToOne(fetch = FetchType.EAGER)
    private Order order;
    
    // Getter and setter
}

Forcing the Fetching Strategy in JPQL

Another factor that can influence your fetching strategy is how you write your JPQL statements to fetch your Entity graph.

For example:

TypedQuery<Order> query = entityManager.createQuery(
    "SELECT DISTINCT o FROM Order o JOIN FETCH o.items", Order.class);
List<Order> orders = query.getResultList();

In this example, the JPQL query explicitly uses a JOIN FETCH clause to eagerly load the associated items collection of the Order entity. This ensures that the items are fetched from the database along with the Order entity in a single query, reducing the need for lazy loading.

Let’s see another example:

TypedQuery<Order> query = entityManager.createQuery(
    "SELECT o FROM Order o", Order.class);
List<Order> orders = query.getResultList();

In this example, the JPQL query simply selects the Order entities without specifying any eager loading directives. As a result, the associated items collection will be lazily loaded when accessed later, following the default lazy loading behavior of the JPA provider.

Finally, here is another example:

TypedQuery<Order> query = entityManager.createQuery(
    "SELECT o FROM Order o LEFT JOIN o.items", Order.class);
List<Order> orders = query.getResultList();

In this example, the JPQL query uses a LEFT JOIN to include the associated items collection of the Order entity. However, it does not use the FETCH keyword, indicating that the items should be lazily loaded when accessed later.

It’s important to note that JPQL queries can influence the loading behavior only up to a certain extent. The final loading behavior also depends on the fetch type defined in the entity mappings and the configuration of the JPA provider.

By structuring your JPQL queries, you can control the eager or lazy loading of associated entities based on your specific requirements.

Finally, to learn more about tuning Hibernates fetches, check this article: Hibernate fetching performance tuning

Conclusion

We have covered the two ways to fetch data in Entity relations. Which one works the best? it depends on the usage of the Entity relation.

EAGER fetching can be very efficient because all Entities and their relations are fetched with only one query. But it can create a large overhead if your related Entities are large and not needed in all use cases.

LAZY fetching delays the initialization of the relationship until you are using the related Entity in your code. The drawback of this approach is that the JPA engine needs to execute an additional query to initialize each relationship.