In this article, I will show the subtle differences between
This is the 4th post in the Hibernate hard facts focus series.Other posts include:
- Hibernate hard facts part 1
- Hibernate hard facts part 2
- Hibernate hard facts part 3
- Hibernate hard facts - Part 4 (this post)
- Hibernate hard facts – Part 5
- Hibernate hard facts - Part 6
- Hibernate hard facts – Part 7
Hibernate, like life, can be full of suprises. Today, I will share one with you: have you ever noticed that Hibernate provides you with 2 methods to load a persistent entity from the database tier? These two methods are
get(Class, Serializable) and
load(Class, Serializable) of the Session class and their respective variations.
Strangely enough, they both have the same signature. Strangely enough, both of their API description starts the same:
Return the persistent instance of the given entity class with the given identifier.
Most developers use them indifferently. It is a mistake since, if the entity is not found,
get() will return
load() will throw an Hibernate exception. This is well described in the API:
Return the persistent instance of the given entity class with the given identifier, assuming that the instance exists. You should not use this method to determine if an instance exists (use get() instead). Use this only to retrieve an instance that you assume exists, where non-existence would be an actual error.
Truth be told, the real difference lies elsewhere: the
get() method returns an instance, whereas the
load() method returns a <em>proxy</em>. Not convinced? Try the following code snippet:
Session session = factory.getCurrentSession(); Owner owner = (Owner) session.get(Owner.class, 1); // Test the class of the object assertSame(owner.getClass(), Owner.class);
The test pass, asserting that the owner’s class is in fact Owner. Now, in another session, try the following:
Session session = factory.getCurrentSession(); Owner owner = (Owner) session.load(Owner.class, 1); // Test the class of the object assertNotSame(owner.getClass(), Owner.class);
The test will pass too, asserting that the owner’s class is not
Owner. If you spy the object in the debugger, you’ll see a Javassist proxyed instance and that fields are not initialized! Notice that in both cases, you are able to safely cast the instance to
Owner. Calling getters will also return expected results.
Why call the
load() method then? Because since it is a proxy, it won’t hit the DB until a getter method is called.
Moreover, these features are also available in JPA from the
EntityManager, respectively with the
Yet, both behaviours are modified by Hibernate’s caching mechanism. Try the following code snippet:
// Loads the reference session.load(Owner.class, 1); Owner owner = (Owner) session.get(Owner.class, 1);
According to what was said before, owner’s real class should be the real McCoy. Dead wrong! Since Hibernate previously called
get() looks in the
Session cache (the 1st level one) and returns a proxy!
The behaviour is symmetrical with the following test, which will pass although it’s counter-intuitive:
// Gets the object session.get(Owner.class, 1); // Loads the reference, but looks for it in the cache and loads // the real entity instead Owner owner = (Owner) session.load(Owner.class, 1); // Test the class of the object assertSame(owner.getClass(), Owner.class);
Conclusion: Hibernate does a wonderful job at making ORM easier. Yet, it’s not an easy framework: be very wary for subtle behaviour differences.
The sources for the entire hard facts series is available here in Eclipse/Maven format.