Background
The topic of Fetch types in Hibernate is a fairly advanced topic, as it requires you to have a decent understanding of the Java programming language, as well as have had exposure to SQL and the Hibernate framework for Java. In this post I'm going to assume that you posses this knowledge, if you do not, then I suggest you follow along with my podcasts available here.
What the heck is a Fetch Type?
Great question! Hibernate is a very handy framework for removing your need to fully understand SQL, but it will force you to understand things such as joins.
Joining two tables in SQL is the foundation of a relational database, as joins allow you to actually define relationships between tables (objects).
Having said that, relationships are important to understand when talking about fetch types in Hibernate. This is the case because whenever you define a relationship in Hibernate, you'll also need to define the fetch type. The fetch type essentially decides whether or not to load all of the relationships of a particular object/table as soon as the object/table is initially fetched.
An example of this would be as follows, consider this User
object:
public class User { import javax.persistence.OneToOne; import javax.persistence.JoinColumn; private String username; private String password; private Profile userProfile; // omitting code for getters and setters for username, password @OneToOne @JoinColumn(name="user_profile_id") private Profile getUserProfile() { return userProfile; } private void setUserProfile(Profile userProfile) { this.userProfile = userProfile; } }
Can you spot the relationship in this User
object?
If you can't, no worries, there's an easy way to spot it!
Any time you see a @OneToOne
, @OneToMany
or @ManyToMany
annotations, you've got a relationship. What's important to note is that the fetch type should be specified within those annotations, if you don't specify one then it defaults to FetchType.LAZY
.
What this means is that when you load up an instance of the User
class via a Hibernate query, Hibernate will NOT load this User's Profile
unless you explicitly ask it to. So this means that if you try to call user.getUserProfile()
, you'll get a NULL
.
Difference Between EAGER and LAZY
Okay, so we talked about the fact that FetchType.LAZY
is the default fetch type for all Hibernate annotation relationships. We also talked about the fact that when you use the Lazy fetch type, Hibernate won't load the relationships for that particular object instance. So then, what's the difference between Eager and Lazy?
Fetch type Eager is essentially the opposite of Lazy, Eager will by default load ALL of the relationships related to a particular object loaded by Hibernate. This means that if you change the relationship to be this:
import javax.persistence.FetchType; //.... //.... //.... @OneToOne(fetch=FetchType.EAGER) @JoinColumn(name="user_profile_id") private Profile getUserProfile() { return userProfile; }
Hibernate will now load the user profile into the user object by default. This means that if you access user.getUserProfile()
it won't be NULL
(unless the joining table actually doesn't contain a matching row by the “user_profile_id” join column).
So, the long story short here is:
FetchType.LAZY
= Doesn't load the relationships unless explicitly “asked for” via getter
FetchType.EAGER
= Loads ALL relationships
Pros and Cons
If using the lazy loading approach can cause NullPointerException
s, then why the heck would you want to use it? Well, let's just take a minute to think about this.
Let's assume we had a User
object that has 10 different relationships to other objects. Let's also say that you have a user that's trying to login and you just want to check to see if their username and password is correct and matches what's stored in the database. Would you really want to load ALL 10 relationships and NOT make use of ANY of those loaded relationships? Remember, loading things from a database is fairly “expensive” in terms of the time it takes to load all that information into a Java objects AND keep them in memory. And what happens if one of the relationships points to an object that ALSO has 10 relationships (and all of those are set up as fetch type EAGER). Think of the cascading massacre of objects loaded from the database into memory!
The way I like to put it is like this:
-
- EAGER: Convenient, but slow
- LAZY: More coding, but much more efficient
How to Properly use Lazy Loading w/ Spring
So having outlined the differences between these two approaches, I hope you realize that Lazy loading is a suggested route to take your application code in most scenarios. So having said that, how do we make use of Lazy loading with a Spring enabled application?
The important thing to remember is that when using a Spring/Hibernate application, there's a bit of code that is handled for you in the background that you don't actually see. The code that you don't see is the transactional code (enabled by the @Transactional
annotation in your DAOs) and this is something that you always need to keep in mind when using Lazy loading.
Having said that, let's take a look at an example of Lazy loading using our User
object. Let's assume we have our User
object which has a Lazy load fetch enabled to the Profile
object. So we will need to design our UserDao
with this relationship in mind. One approach is to create two separate getter methods like so:
/** * This UserDao object is being constructed under the assumption that the relationship * to the Profile object is being Lazily loaded using FetchType.LAZY */ @Repository @Transactional public class UserDao { @Autowired private SessionFactory sessionFactory; public User getUserById(Long userId) { Session session = sessionFactory.getCurrentSession(); // this will invoke the return (User) session.createCriteria(User.class).add(Restrictions.idEq(userId)).uniqueResult(); } public User getUserWithProfileById(Long userId) { Session session = sessionFactory.getCurrentSession(); User user = (Users) session.createCriteria(User.class).add(Restrictions.idEq(userId)).uniqueResult(); // this will force SQL to execute the query that will join with the user's profile and populate // the appropriate information into the user object. Hibernate.initialize(user.getUserProfile()); return user; } }
In the code above you see that we have created two getter methods to load the User
object from our database via Hibernate.
This means that when you want to load a User
object without any relationships (i.e. when you're checking to see if the login information is valid) then you can call getUserById
and this will bypass the unnecessary join to the Profile
object/table. But if you are in a situation where you DO need to refer to the User
‘s profile, then you can call the getUserWithProfileById
.
Make sense?
Fun Fact
I was struggling with this Lazy loading concept at first and I'll share the secret as to why. If you are using lazy loading and you were to invoke the getUserById
method, you know that it won't load the profile relationship… but what happens if you've inserted a breakpoint into this getUserById
method and you CLICK on the user's profile listing in the “Variables” tab inside of Eclipse/STS?
Hibernate will automatically invoke that SQL join and populate the relationship for you!
This was confusing the heck out of me because when I clicked on the profile, it was showing up just fine in the “variables” section, and then my subsequent code was working normally (I wasn't getting NullPointerException
s) This was so strange and it wasn't until I looked at the console log that I realized as SOON as I clicked on that profile property, hibernate was executing the SQL query and populating the object.
So be wary, it's almost like a quantum physics problem… the act of observing lazy loading changes the outcome of the code flow!
Also, be sure to grab the free Video Tutorial for Eager vs Lazy Loading in Hibernate
And if you'd like more help and guidance in becoming a full stack Java developer, considering joining our Bootcamp, which re-opens again soon.
The Best 7 Java Programming Resources
Alright, so if you're read this far down the article, then you're clearly interested in learning how to code. We actually have a free guide that outlines the best 7 resources for Java programmers.
When you sign up you'll receive the guide immediately and you'll also receive a few emails a week that will help you speed up your Java learning process so you can get up and running as fast as possible.
Go ahead an click the button below to get started!
CAREFUL! Don't Push This Button If You're Not Ready To FINALLY Learn How to Code