View RSS Feed

My Java Tips

Lazy associations

Rate this Entry
by , 06-02-2011 at 08:26 PM (2427 Views)
Hibernate uses lazy select fetching by default for collections and lazy proxy fetching for single-valued associations. This default behavior makes sense for almost all associations in almost all applications.

Hibernate will use the batch fetch optimization for lazy fetching if hibernate.default_batch_fetch_size parameter is set. This optimization may also be enabled at a more granular level.

Lazy fetching introduces a problem that should be taken care of. Remember that access to a lazy association outside of the context of an open Hibernate session will result in an exception. Review the code snippet bellow:

Java Code:
s = sessions.openSession();
Transaction tx = s.beginTransaction();
User u = (User) s.createQuery("from User u where u.name=:userName").setString("userName", userName).uniqueResult();
Map permissions = u.getPermissions();
tx.commit();
s.close();

Integer accessLevel = (Integer) permissions.get("accounts"); // Error!
The last line of code will report an exception because the permissions collection was not initialized when the Session was closed. Therefore, the collection will not be able to load its state.

Remember that hibernate does not support lazy initialization for detached objects.

Considering the problem with the code presented in the last post, we need to fix the problem. We need to move the code that reads from the collection to just before the transaction is committed. Also one can use a non-lazy collection or association, by specifying lazy="false" for the association mapping. It is usually preferred that lazy initialization be used for almost all collections and associations.

Do remember that if you have too many non-lazy associations in your object model, Hibernate might end up needing to fetch the entire database into memory in every transaction, which is not at all required. We often want to choose join fetching, non-lazy by nature, instead of select fetching in a particular transaction.

Select fetching is the default one and it is extremely vulnerable to N+1 selects problems. For this reason, we should enable join fetching in the mapping document.

Review the snippet below:

XML Code:
<set name="permissions"
fetch="join">
<key column="userId"/>
<one-to-many class="Permission"/>
</set
<many-to-one name="mother" class="Cat" fetch="join"/>
This fetch strategy in the mapping document will affect the following:

  • retrieval via get() or load()
  • retrieval that happens implicitly when an association is navigated
  • Criteria queries
  • HQL queries if subselect fetching is used


Remember that what ever fetching strategy you use, the defined non-lazy graph is guaranteed to be loaded into memory. This might result in several immediate selects being used to execute a particular HQL query.

In normal routine, we do not use the mapping document to customize fetching. What we do is, we keep the default behavior, and override it for a particular transaction,. This is done by using left join fetch in HQL. This actually tells Hibernate to fetch the association eagerly in the first select, using an outer join.

If you want to change the fetching strategy used by get() or load(), then use a Criteria query, for example:

Java Code:
User user = (User) session.createCriteria(User.class)
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();
This is s way to avoid problems with N+1 selects is to use the second-level cache.

Submit "Lazy associations" to Facebook Submit "Lazy associations" to Digg Submit "Lazy associations" to del.icio.us Submit "Lazy associations" to StumbleUpon Submit "Lazy associations" to Google

Comments