First rich, then lazy? I suppose that could be a plausible story about someone, as long as it happened in that order. But this really is an object relational mapping topic of some importance. As your data model grows, adding associations between objects and tables, your program gains power, which is great. But you often end up with a large fraction of your objects somehow linked to each other. So what happens when you load one of the objects that is part of a huge interrelated cluster? Since, as you've seen, you can move from one object to its associated objects just by traversing properties, it seems you'd have to load all the associated objects when you load any of them. For small databases this is fine, but in general your database can't hold a lot more than the memory available to your program. Uh oh! And even if it does all fit, rarely will you actually access most of those objects, so it's a waste to load them all.
Luckily, this problem was anticipated by the designers of object/relational mapping software, including Hibernate. The trick is to configure some associations to be 'lazy,' so that associated objects aren't loaded until they're actually referenced. Hibernate will instead make a note of the linked object's identity and put off loading it until you actually try to access it. This is often done for collections like those we've been using.
5.1.1 How do I do that?
With collections, all you need to do is set the lazy attribute in the mapping declaration. For example, our track artists mapping could look like Example 5-1.
Example 5-1. Lazily initializing the track artist associations
<set name="artists" table="TRACK_ARTISTS" lazy="true" > <key column="TRACK"/> <many-to-many class="com.oreilly.hh.Artist" column="ARTIST"/> </set>
This would tell Hibernate to use a special lazy implementation of Set that doesn't load its contents from the database until you actually try to use them. This is done completely transparently , so you don't even notice it's taking place in your code.
Well, if it's that simple, and avoids problems with loading giant snarls of interrelated objects, why not do it all the time? The problem is that the transparency breaks down once you've closed your Hibernate session. At that point, if you try to access content from a lazy collection that hasn't been initialized (even if you've assigned the collection to a different variable, or returned it from a method call), the Hibernate-provided proxy collection can no longer access the database to perform the deferred loading of its contents, and it is forced to throw a LazyInitializationException .
Conservation of complexity seems almost like a law of thermodynamics .
Because this can lead to unexpected crashes far away from the Hibernatespecific code, lazy initialization is turned off by default. It's your responsibility to think carefully about situations in which you need to use it, and ensure that you are doing so safely. The Hibernate reference manual goes into a bit of detail about strategies to consider.
5.1.2 What about...
...Laziness outside of collections? Caching and clustering?
It's easy to see how lazy collections can be supported, since Hibernate can provide its own special implementations of the various Collection interfaces. But what about other kinds of associations? They might benefit from on-demand loading as well.
In fact, Hibernate does support this, and almost as easily (at least from our perspective as users of its services). The way you set this up is by marking an entire persistent class as lazy="true" (this attribute goes right in the class tag of the mapping document). When you do this, Hibernate will generate a proxy class that extends (and poses as) your data class. This lazy proxy puts off actually loading the data until it is needed. Any other objects with associations to the lazy class will sneakily be given these proxy objects, rather than references to your actual data object. The first time any of the methods of the proxy object are used, it will load the real data object and delegate the method call to it. Once the data object is loaded, the proxy simply continues delegating all method calls to it.
If you want to get fancier, you can specify a specific class (or interface) to be extended (or implemented) by the proxy class, using the proxy attribute. The lazy attribute is shorthand for specifying the persistent class itself as the type to be proxied . (If this is all incomprehensible, don't worry, that just means you don't yet need this capability. By the time you do, you'll understand it!)
Naturally, the same caveats about taking care to load anything you'll need to use before closing the session apply to this kind of lazy initialization too. If you need it, you can use it, but do so with care and planning.
The Hibernate reference documentation discusses these considerations in more depth in its chapter 'Improving Performance.' Also introduced there is the fact that Hibernate can be integrated with JVM-level or even clustered object caches to boost the performance of large, distributed applications, by reducing the bottleneck of database access. When plugged in to such a cache, the mapping document lets you configure the cache behavior of classes and associations using (appropriately enough) cache tags. These configurations go beyond what we cover in this notebook, but you should be aware that they're possible in case your application would benefit from them.
Oh, right, that's what we were going to try...