Hibernate Lazy Loading Options for Collections
I have recently been working on a project looking for memory leaks. (BTW, I am extremely happy with the tools available in the JDK – jconsole, jmap, jhat). While poking around at some changes we could make in our code I kept finding quite a few Hibernate objects hanging around. One in particular was the number of logging objects (we log user actions in the db) that were in memory just for a login step. Examining the code there was no apparent flaw. Putting in some debugging statements showed a tremendous amount of logging objects being instantiated during the parent.getChildren() type of statement in the code that adds the logging event to the database table.
If you find the documentation somewhere (I have a personal nit that I think most of the Hibernate documentation is atrocious. I can NEVER find what I am looking for) you will see that the lazy attribute for a set has three choices:
- false
- true (default)
- extra
false means load the collection when the primary object is loaded. I think everyone expected that.
true means wait until some code asks for a collection. Then load the entire set! I don’t think most people expected that.
extra means return a Hibernate interceptor for the collection to the caller and only load individual members of the set when explicitly called for. I think this is the one most people expect to have happen.
We have been running with Hibernate for over a year. Like everyone else we thought we were taking advantage of lazy loading of collections. We have code all over the application that looks like:
Child child = new Child()
child.setParent(parent)
parent.getChildren().add(child)
session.flush()
Of particular note is the parent.getChildren statement. This function call is intercepted by Hibernate and we thought would lazy load the collection. Our mapping for the set in the Parent mapping had no setting for lazy, as the documentation says that lazy is on by default.
This means in our code where we just wanted to add a logging event we were loading the entire logging event table every time we added a record to the table!
We have changed all of our one-to-many sets to use extra. So far we are guessing that the many-to-many sets will likely traverse every matching member so the default true setting is good.
