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 Strategy | Default strategy |
---|---|
OneToOne | EAGER |
ManyToOne | EAGER |
OneToMany | LAZY |
ManyToMany | LAZY |
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.
Found the article helpful? if so please follow us on Socials