Sunday, July 22, 2007

Transaction management with Spring / Hibernate + ZK

Normal Spring transaction management for Hibernate does not work out of the box with ZK. Actually to be exact the transaction management works fine, but the Hibernate session is not kept open after the transaction until the end of the request as it should when using OpenSessionInViewFilter. The end result is that you get LazyInitializationException when trying to access proxies, since the session is closed.

To fix this, a file called WEB-INF/zk.xml must be created with the following contents:
<zk>
<listener>
<description>ThreadLocal Variables Synchronizer</description>
<listener-class>org.zkoss.zkplus.util.ThreadLocalListener</listener-class>
</listener>

<preference>
<name>ThreadLocal</name>
<value>
org.springframework.transaction.support.TransactionSynchronizationManager=resources,synchronizations,currentTransactionName,currentTransactionReadOnly,actualTransactionActive;
org.springframework.orm.hibernate3.SessionFactoryUtils=deferredCloseHolder;
org.springframework.transaction.interceptor.TransactionAspectSupport=transactionInfoHolder;
</value>
</preference>
</zk>



The problem is that the Spring OpenSessionInViewFilter opens a session, and binds it to the request's thread. However, ZK uses a multi-threaded event driven approach, so the actual processing happens in a new thread that gets kicked off by the ZK servlet - this breaks the connection to the open session. Adding the above listener copies the ThreadLocal variables with the session etc from the request thread's context into the event thread. As you can see, more prefs can be added to copy other objects from the ThreadLocal context.

I had to inspect a lot of code to fix this, turns out it was documented in great detail all along (but sort of all over the place):


Another possible solution is to make the ZK servlet perform all processing within its own thread, this was a new feature in ZK 2.4. I haven't tried this though and it looks like this may be dangerous, since the thread will block the entire servlet if it blocks for any reason. To enable this, the following is required in the zk.xml:

<system-config>
<disable-event-thread/>
</system-config>

3 comments:

Anonymous said...

Thanks Andre for this. I couldn't get Spring OSIV working with ZK until I ran across your blog!

For the sake of completeness, I include my snippet from web.xml:

<!-- Hibernate OpenSession Filter -->
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactoryHBE</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


Together with the ThreadLocalListener in zk.xml it does the trick!

I also tried the disable-event-thread configuration but it rendered our ZK application undeployable. I can hardly think that it could be helpful for anyone.

RedVaX said...

Can anyone send me your entire zk.xml and web.xml?
I really need to resolve this issue very soon.
Thanks,
Celso

Anonymous said...

Who knows where to download XRumer 5.0 Palladium?
Help, please. All recommend this program to effectively advertise on the Internet, this is the best program!