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.
Don’t want to read? You can watch it here!
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.
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 OrderItem and Product entity.
When I now fetch an OrderItem entity from the database, Hibernate will also get the related Product entity.
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.
The used FetchType has no influence on the business code. You can call the getOrderItems() method just as any other getter method.
Hibernate handles the lazy initialization transparently and fetches the OrderItem entities as soon as the getter method gets called.
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.
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.