Friday, August 11, 2006

How to outsmart lazy loading

A customer recently had a problem with his O/R mapping framework(*) that all relations between objects/classes were marked to be loaded lazily. Despite this setting, loading a class (either by direct access or via HQL) caused all related classes to be loaded and their related ones as well until the whole graph of attached objects was in memory. The trace of the generated SQL clearly showed this behaviour without any explanation.

Looking at the mapping files, the queries or the java code for the relations brought not explanation. Also the startup of the mapper did not show any errors. The customer even implemented equals() and hashCode().

After having a closer look at the later two methods, the problem came clear: Hibernate did in deed only load the starting class and not the relations (initially). But when putting the entity into the Session, Hibernate has to call equals() or hashcode() for its internal maps. Those two methods now have been implemented my using (amongst other) some relation fields. So in order to compute the hashCode(), the relation had to be loaded (which in turn loaded the next relations, as the hashCode() method on the other classes were implemented in a similar manner).
So actually Hibernate worked as designed :-)

The use of collections within hashCode()/equals() is problematic anyway: a Map uses it to determine the location of the entry. If the hashcode changes this location is no longer correct, which will lead to strange problems.

As a summary: don't only be careful with externally generated primary keys within equals() and hashCode(), but also with using collections.

(*) Actually this was with Hibernate, but the problem can also arise with JDO, JPA etc.

No comments: