Issue:
HibernateInterceptor throws the following exception when there is a transaction timeout inside Weblogic:
java.lang.IllegalStateException: Already value
[org.springframework.orm.hibernate3.SessionHolder@9 f7af3c] for key
[org.hibernate.impl.SessionFactoryImpl@ae6e03a] bound to thread
[ExecuteThread: '15' for queue: 'weblogic.kernel.Default']
at org.springframework.transaction.support.Transactio nSynchronizationManager.bindResource(Ljava.lang.Ob ject;Ljava.lang.ObjectV(TransactionSynchronizati onManager.java:156)
at org.springframework.orm.hibernate3.HibernateInterc eptor.invoke(Lorg.aopalliance.intercept.MethodInvo cationLjava.lang.Object;(HibernateInterceptor.ja va:90)
at org.springframework.aop.framework.ReflectiveMethod Invocation.proceed()Ljava.lang.Object;(ReflectiveM ethodInvocation.java:144)
at org.springframework.aop.framework.JdkDynamicAopPro xy.invoke(Ljava.lang.Object;Ljava.lang.reflect.Met hod;[Ljava.lang.ObjectLjava.lang.Object;(JdkDynamicAo pProxy.java:174)
Cause:
In a case of a transaction timeout, Weblogic executes the rollback and transaction synchronization callbacks on a seperate thread. This causes an empty SessionHolder object to still be associated with the old thread. This in itself is not a problem and I see it is well documented in Spring's SpringSessionSynchronization class(method: beforeCompletion).
However, the problem is when the same thread is used for a new request in a non-transactional scope(EJB method with transaction NotSupported). When this happens,from what I understood by stepping through the SessionFactoryUtils.doGetSession method, Spring does not reuse the old SessionHolder attached to this reused thread. In other words, it does not put the new Session created into the already existing SessionHolder. This causes the SessionFactoryUtils.isSessionTransactional method to return false. Thus, HibernateInterceptor now tries to create a new Session Holder, and in the process throws the above mentioned exception.
This, however would work fine if the new request on this thread, would have started in a scope of a transaction.Then I see SessionFactoryUtils.registerJtaSynchronization puts this new Session in the old SessionHolder.
Proposed Solution:
Change the SessionFactoryUtils.doGetSession method to put the new Session in the old SessionHolder(if it exists), regardless of a request being in a JTA transaction or not.
Juergen