AddThis

Tuesday, 3 July 2018

Entity Mappings: Introduction to JPA / Hibernate FetchTypes

The FetchType defines when Hibernate gets the related entities from the database, and it is one of the crucial elements for a fast persistence tier. In general, you want to fetch the entities you use in your business tier as efficiently as possible. But that’s not that easy. You either get all relationships with one query or you fetch only the root entity and initialize the relationships as soon as you need them.
I’ll explain both approaches in more detail during this post and also provide you some links to more advanced solutions that combine flexibility and efficiency.
Default FetchTypes and how to change it
When you started with Hibernate, you most, likely either didn’t know about FetchTypes or you were told to always use FetchType.LAZY. In general, that’s a good recommendation. But what does it exactly mean? And what is the default if you don’t define the FetchType?
The default depends on the cardinality of the relationship. All to-one relationships use FetchType.EAGER and all to-many relationships FetchType.LAZY.
Even the best default doesn’t fit for all use cases, and you sometimes want to change it. You can do this by providing your preferred FetchType to the relationship annotation as you can see in the following code snippet.
@Entity
@Table(name = "purchaseOrder")
public class Order implements Serializable {
  @OneToMany(mappedBy = "order", fetch = FetchType.EAGER)
  private Set<OrderItem> items = new HashSet<OrderItem>();
  ...
}
OK, now let’s have a more detailed look at the different FetchTypes. 
FetchType.EAGER – Fetch it so you’ll have it when you need it
The FetchType.EAGER tells Hibernate to get all elements of a relationship when selecting the root entity. As I explained earlier, this is the default for to-one relationships, and you can see it in the following code snippets.
I use the default FetchType (EAGER) for the many-to-one relationship between the OrderItemand Product entity.
@Entity
public class OrderItem implements Serializable
{
   @ManyToOne
   private Product product;  
   ...
}
When I now fetch an OrderItem entity from the database, Hibernate will also get the related Product entity.
OrderItem orderItem = em.find(OrderItem.class, 1L);
log.info("Fetched OrderItem: "+orderItem);
Assert.assertNotNull(orderItem.getProduct());
view rawUseEagerFetching.java
05:01:24,504 DEBUG SQL:92 - select orderitem0_.id as id1_0_0_, orderitem0_.order_id as order_id4_0_0_, orderitem0_.product_id as product_5_0_0_, orderitem0_.quantity as quantity2_0_0_, orderitem0_.version as version3_0_0_, order1_.id as id1_2_1_, order1_.orderNumber as orderNum2_2_1_, order1_.version as version3_2_1_, product2_.id as id1_1_2_, product2_.name as name2_1_2_, product2_.price as price3_1_2_, product2_.version as version4_1_2_ from OrderItem orderitem0_ left outer join purchaseOrder order1_ on orderitem0_.order_id=order1_.id left outer join Product product2_ on orderitem0_.product_id=product2_.id where orderitem0_.id=?
05:01:24,557  INFO FetchTypes:77 - Fetched OrderItem: OrderItem , quantity: 100
view rawEagerFetchingLog.log
This seems to be very useful in the beginning. Joining the required entities and getting all of them in one query is very efficient.
But keep in mind, that Hibernate will ALWAYS fetch the Product entity for your OrderItem, even if you don’t use it in your business code. If the related entity isn’t too big, this is not an issue for to-one relationships. But it will most likely slow down your application if you use it for a to-many relationship that you don’t need for your use case. Hibernate then has to fetch tens or even hundreds of additional entities which creates a significant overhead.  
FetchType.LAZY – Fetch it when you need it
The FetchType.LAZY tells Hibernate to only fetch the related entities from the database when you use the relationship. This is a good idea in general because there’s no reason to select entities you don’t need for your uses case. You can see an example of a lazily fetched relationship in the following code snippets.
The one-to-many relationship between the Order and the OrderItem entities uses the default FetchType for to-many relationships which is lazy.
@Entity
@Table(name = "purchaseOrder")
public class Order implements Serializable {
  @OneToMany(mappedBy = "order")
  private Set<OrderItem> items = new HashSet<OrderItem>();
  ...
}
view rawOrder.java
The used FetchType has no influence on the business code. You can call the getOrderItems() method just as any other getter method.
Order newOrder = em.find(Order.class, 1L);
log.info("Fetched Order: "+newOrder);
Assert.assertEquals(2, newOrder.getItems().size());
view rawUseLazyFetching.java
Hibernate handles the lazy initialization transparently and fetches the OrderItem entities as soon as the getter method gets called.
05:03:01,504 DEBUG SQL:92 - select order0_.id as id1_2_0_, order0_.orderNumber as orderNum2_2_0_, order0_.version as version3_2_0_ from purchaseOrder order0_ where order0_.id=?
05:03:01,545  INFO FetchTypes:45 - Fetched Order: Order orderNumber: order1
05:03:01,549 DEBUG SQL:92 - select items0_.order_id as order_id4_0_0_, items0_.id as id1_0_0_, items0_.id as id1_0_1_, items0_.order_id as order_id4_0_1_, items0_.product_id as product_5_0_1_, items0_.quantity as quantity2_0_1_, items0_.version as version3_0_1_, product1_.id as id1_1_2_, product1_.name as name2_1_2_, product1_.price as price3_1_2_, product1_.version as version4_1_2_ from OrderItem items0_ left outer join Product product1_ on items0_.product_id=product1_.id where items0_.order_id=?
view rawLazyFetchingLog.log
Handling lazy relationships in this way is perfectly fine if you work on a single Order entity or a small list of entities. But it becomes a performance problem when you do it on a large list of entities. As you can see in the following log messages, Hibernate has to perform an additional SQL statement for each Order entity to fetch its OrderItems.
05:03:40,936 DEBUG ConcurrentStatisticsImpl:411 - HHH000117: HQL: SELECT o FROM Order o, time: 41ms, rows: 3
05:03:40,939  INFO FetchTypes:60 - Fetched all Orders
05:03:40,942 DEBUG SQL:92 - select items0_.order_id as order_id4_0_0_, items0_.id as id1_0_0_, items0_.id as id1_0_1_, items0_.order_id as order_id4_0_1_, items0_.product_id as product_5_0_1_, items0_.quantity as quantity2_0_1_, items0_.version as version3_0_1_, product1_.id as id1_1_2_, product1_.name as name2_1_2_, product1_.price as price3_1_2_, product1_.version as version4_1_2_ from OrderItem items0_ left outer join Product product1_ on items0_.product_id=product1_.id where items0_.order_id=?
05:03:40,957 DEBUG SQL:92 - select items0_.order_id as order_id4_0_0_, items0_.id as id1_0_0_, items0_.id as id1_0_1_, items0_.order_id as order_id4_0_1_, items0_.product_id as product_5_0_1_, items0_.quantity as quantity2_0_1_, items0_.version as version3_0_1_, product1_.id as id1_1_2_, product1_.name as name2_1_2_, product1_.price as price3_1_2_, product1_.version as version4_1_2_ from OrderItem items0_ left outer join Product product1_ on items0_.product_id=product1_.id where items0_.order_id=?
05:03:40,959 DEBUG SQL:92 - select items0_.order_id as order_id4_0_0_, items0_.id as id1_0_0_, items0_.id as id1_0_1_, items0_.order_id as order_id4_0_1_, items0_.product_id as product_5_0_1_, items0_.quantity as quantity2_0_1_, items0_.version as version3_0_1_, product1_.id as id1_1_2_, product1_.name as name2_1_2_, product1_.price as price3_1_2_, product1_.version as version4_1_2_ from OrderItem items0_ left outer join Product product1_ on items0_.product_id=product1_.id where items0_.order_id=?
view rawLazyFetchingListLog.log
This behavior is called n+1 select issue, and it’s the most common performance problem. It is so common that you most likely have it if you didn’t explicitly search for it. If you’re not sure how to do that, signup for my free, 3-part video course about finding and fixing n+1 select issues.
There are two ways to avoid these issues:
You can use FetchType.EAGER if you know that all of your use cases that fetch an Order entity also need to process the related OrderItem entities. That will almost never be the case.
If there are some use cases which only work on Order entities (which is most likely the case), you should use FetchType.LAZY in your entity mapping and use one of these options to initialize the relationship when you need them.
Summary and cheat sheet
As I said in the beginning, you need to make sure to use the right FetchType for your use case to avoid common Hibernate performance issues. For most use cases, the FetchType.LAZY is a good choice. But make sure that you don’t create any n+1 select issues.
Let’s quickly summarize the different FetchTypes.
EAGER fetching tells Hibernate to get the related entities with the initial query. This can be very efficient because all entities are fetched with only one query. But in most cases it just creates a huge overhead because you select entities you don’t need in your use case.
You can prevent this with FetchType.LAZY. This tells Hibernate to delay the initialization of the relationship until you access it in your business code. The drawback of this approach is that Hibernate needs to execute an additional query to initialize each relationship.

No comments:

Post a Comment

Solving real time queries using java 8 features stream api with examples

package com.pse; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java...