Now it's time to dive into the nitty gritty of Hibernate's mysterious inner workings.
I'm by no means an expert in Hibernate, but I do use it almost every day for my own projects, so I do know a thing or two about how it works.
One topic that had me scratching my head for ages was the Hibernate life cycle. What I mean by the life cycle is the way Hibernate interacts with Java objects at certain points in the existence of said Java objects.
Let's start from the beginning…
What the heck is a Hibernate Life Cycle?
You see, Hibernate is picky about your Java objects. Hibernate prefers your objects to be in a certain “state”, known as the persistent state… this persistent state is one of four different states that exist inside of the hibernate persistence life cycle.
Once you have a firm grasp of the different states that an object can be in (as it pertains to Hibernate) you'll be well on your way to mastering the Hibernate framework.
So let's get this Hibernate persistence life cycle lesson started shall we?
A transient object is one that Hibernate has no awareness of whatsoever. It's the first step on our journey through the life cycle. When you first create a plain old Java object (via the
new keyword) this object can be thought of as being in a transient state.
So really, when you think about it, every object you've ever created (before you started working with Hibernate) could be thought of as being in a transient state. Again, this is the case because the database has absolutely no idea that this object is in existence.
How this “state” applies to Hibernate is that Hibernate will not be able to track transient objects and store them in the database (or update the database based on their values).
In order for the database to properly keep track of objects, is for them to be in the next state of the Hibernate persistence life cycle.
When an object is in a persistent state, Hibernate is totally aware of it and can keep the database synchronized with it's values.
Before we launch into the persistent state, let's talk about how we can go from transient to persistent.
There are two main ways to make the leap from a transient object to a persistent object:
- Loading the object from the database via Hibernate APIs
- Saving the object to the database via Hibernate APIs
Ways to Save an Object
Hibernate has a few different ways to save an object to the database, but the two main ways are as follows:
Invoking either of these Hibernate methods will shift your transient object into the persistent state (so long as the save is successful).
I won't go into detail about how to use these methods, as I've already done so in this post.
Ways to Load an Object
There are quite a few ways to load an object from a database. Here's a couple that I use the most:
I won't go into detail here, as I've already covered how to load data from a database using Hibernate.
Why the Persistent State is King
Hibernate's true abilities are shown when working with persistent objects.
When an object is in a persistent state within a transactional context in Hibernate, magical things happen. Namely, Hibernate will be able to synchronize the persistent object(s) when you manually invoke a save or when the transactional context is closed and committed.
To create this “transactional context” I simply rely on the Spring framework and its
@Transactional annotation. You've seen this used already in a previous post.
But you can certainly create your own transactional context manually if you do not prefer to use the Spring framework. This just means that you'll be writing some boilerplate transaction management code. I'm not an expert in this area at all, so if you're interested in researching it further, there are many articles that cover transactions with Hibernate on the web.
In any case, the point I'm trying to get at here is that the majority of Hibernate's “persistence” functionality occurs with your object is in a persistent state.
This means that if you want to add new data to the database, you'll need to invoke the
session.saveOrUpdate() methods to take your transient object into a persistent state.
Or this means that if you want to update existing data in the database, you'll first need to load it into an object via the aforementioned
Only when you're modifying / updating persistent objects will Hibernate actually synchronize your changes from the Java object to your database.
Okay, so you know that the persistent state is key, but you can only be in this state while you're inside of a transactional context.
In the world of Spring, this means that you will only be in a persistent state while inside of your DAO's “save” methods. This is the case because Spring wraps a transactional context around each method in your DAO class. This happens because we used the
@Transactional annotation at the class level.
So what happens one the flow of code leaves your DAO's save method? Well, the transactional context is closed (via the Spring code I just talked about) and your object that was previously in the persistent state is now in a detached state.
The detached state is given to an object that was previously “attached” (persistent) but has now left the scope of the transactional context.
The “side effect” of this state is that if you make any changes to your object, Hibernate won't be tracking them and thus won't synchronize the changes to the database. You'll need to invoke a
sesssion.update(), or a
session.merge() in order to re-attach it and make it persistent again in a new transactional context.
This is where things can get a bit wonky though.
You see, since your object was previously persistent, Hibernate still holds a record of this object in its memory. So when your (now detached) object is passed into a
session.update() method, Hibernate tries to update only the records that have changed since it last “knew” your object.
There are times when Hibernate just doesn't know how to handle the updates and can do nothing except throw exceptions. I've hit this scenario before and it's quite annoying.
Again, I'm not an expert in this field, so if anyone can explain exactly what's going on in this particular scenario, then by all means leave a comment below.
In any case, the solution is to use the
session.merge() method instead. What the
merge() does is just overwrite everything (thus essentially ignoring what's in Hibernate's memory) with the object that you're passing into the
Finally, the last state in Hibernate's persistence life cycle is the “removed” state. This happens when you have a persistent object that you've flagged to be deleted.
To delete a persistent object, you just invoke the
session.delete() method and pass in your persistent object.
Note that you cannot properly delete a non-persistent object, so to properly delete an object you'll need to load it using the methods we discussed earlier (going from transient to persistent).
Once you've deleted an object and moved to the “removed” state, you should no longer use that particular object for any reason.
Hibernate Persistence Life Cycle Diagram
If you would like to grab a copy of a really helpful diagram that outlines all the steps in the life cycle, you can download the cheat sheet here.
You'll be able to print out this cheat sheet and stick in on your wall to impress your Mom and your friends.