Chop Hibernate Lazy Associations
<!–[if gte mso 9]>
In many Java application architectures, the domain model is been used by the presentation tier as the “Model” in MVC architecture. This can be done with various Java Web Framework: Spring-MVC, JSF, GWT etc.
And here comes the problem…
If you reuse your Hibernate entities as an output of your api to the client, you will probably probably face “org.hibernate.LazyInitializationException”.
The reason for this, is the way Hibernate load entities from the DB: Whenever you use a persistent Collection or MAP in your domain model, Hibernate will use its own implementation, and by default will NOT load this map or collection, unless you explicitly load it, or request it in your query. More than that – “ManyToOne” and “OneToOne” association can also be declared as lazy. In this case Hiberante will use a Proxy, which will act as a “lazy” mediator, and will load the lazy association only upon request.
In general, this Hibernate “laziness” approach is great – You can load lazy associations on the server while your code is in the transaction boundaries. As long as your presentation layer is in the same address space as the server, it doesn’t make any problem to the client - The presentation layer will render only the loaded entities graph, which was fetched by Hibernate at the server tier.
There are several approaches to this. One of them is simply decouple the domain model completely from the clients by using yet another layer – DTO (Data Transfer Objects). This decouple is an advantage, since there is a separation between the client and server code. However, personally, I don’t like them much, since they put a parallel hierarchy to the domain model. A change in one place usually makes a change in the second hierarchy, and there is a lot of code to translate from and back the domain model to the DTOs. Even with libraries like Dozer, you will still have to maintain a parallel hierarchy.
Many times it’s easier to use the same model all over. So I looked for a solution that “chop” all lazy associations, just before marshaling the entities to the client. Since I haven’t found any library that does it, I decided to develop such a “chopping library”, and this is what we did in one of our customers.
We called it “Hibernate Lazy Chopper” – and the idea is simple: Whenever the results comes out of the server to the client, just before the serialization framework apply its marshaling, the framework goes over all the object graph recursively, and “chop” lazy associations. This chopping works on both Hibernate lazy collections and Hibernate lazy proxies, and uses Spring AOP mechanism.
Here are the technical details, for how you should use the lazy chopper:
1. Add Tikal Maven Repository with the following URL : http://network.tikalk.com/release/repository
<repositories> <repository> <id>tikalnet-release</id> <name>tikalnet-release</name> <url> http://network.tikalk.com/release/repository </url> </repository> </repositories>
2. Add it as a Maven dependency to your project:
<dependency> <groupId>com.tikal.lazychopper</groupId> <artifactId>lazy-chopper</artifactId> <version>1.1.8</version> </dependency>
3. Add lazy chopper beans to your application by including its application context file:
4. Declare 2 properties on your application properties file (I assume you already included this file with PropertyPlaceHolderConfigurer)
a. modelpackage - This is the package to scan lazy entities on your domain model. For example:
b. abstractEntityClass – Your base class for your entities, that each entity that is scanned for lazy associations should be inherited from. For example:
5. Declare a pointcut named “allServices” in your application context that represent your service layer. This Pointcut will be use to apply the lazy chopping – When a service in this layer finishes, the lazy chopper will apply the chopping the output graph. Here is an example
<aop:config> <aop:pointcut id="allServices" expression="execution(* com.mycompany.myproduct.server.services..*Service.*(..))" /> </aop:config>
6. Configure your transaction advice to be higher order number than 10 (the choppingAdvice’s order value) – This makes sure, the chopping advice should take place **after** the transaction advice. Otherwise the chopped entities would have been be modified in the DB, since they would have been in the transaction context. So change the aop:config above and add the transaction advice as follow:
<aop:config> <aop:pointcut id="allServices" expression="execution(* com.mycompany.myproduct.server.services..*Service.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="allServices" order="200" /> </aop:config>
Now whenever, a client will call your server, you will NOT get the LazyIntinalizationException. You can use this library from against any client or external system to your server, and it does NOT matter which protocol or serialization mechanism is been used.
The sources for the project can be downloaded from GitHub here.