|
From: Andy D. <an...@ma...> - 2005-06-21 21:27:18
|
As I track this problem further, I see that it lies Hibernate3's
SessionFactoryUtils. SessionFactoryUtils has an inner class,
"SpringSessionSynchronization", that is instantiated and registered against
the current transaction when using Spring for session management. During
transaction suspend and resume, this implementation will *always* call
TransactionSynchronizationManager.unbindResource(this.sessionFactory) and
TransactionSynchronizationManager.bindResource(this.sessionFactory,
this.sessionHolder), respectively. When Spring performs a commit, it calls
synchronization methods in this order:
1. beforeCommit
2. beforeCompletion
3. afterCompletion
In beforeCompletion, SpringSessionSynchronization will
unbindResource(this.sessionFactory), and sometimes close the Hibernate
Session. However, SpringSessionSynchronization will not reflect this in its
internal state in any way. Now imagine that a TransactionSynchronization
implementation suspends the transaction in afterCompletion.
SessionFactoryUtils.SpringSessionSynchronization will have its suspend method
called, which will attempt to unbindResource(this.sessionFactory), causing an
exception since it has already been unbound. Moreover, even if it performed
a check and did not unbind the resource, resume() would still attempt to
rebind the resource (whose Hibernate Session was most likely closed in
beforeCompletion), leaving that closed resource bound to the current thread
even after the main transaction has been committed.
Basically, SpringSessionSynchronization needs to be modified to handle this
call sequence:
1. beforeCommit
2. beforeCompletion
3. suspend
4. resume
5. afterCompletion
Which it does not now support (though this was working in older versions of
Spring). I'm willing to open a JIRA issue, make these changes, and submit a
patch if no one has a problem with them.
- Andy
On Monday 20 June 2005 03:28 pm, Andy Depue wrote:
> I'm attempting to do some advanced transaction synchronization. During
> synchronization, I need to update a status row in a DB table. Of course,
> since DB access is itself transactional, I create a new transaction for the
> duration of the update using PROPAGATION_REQUIRES_NEW. The problem occurs
> when I attempt to execute this in "afterCompletion". I sometimes get this
> exception:
> java.lang.IllegalStateException: No value for key
> [org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread [Timer-3]
>
> Here is an abbreviated version of my code:
>
> ------------
> TransactionSynchronizationManager.registerSynchronization(new
> TransactionSynchronization() {
>
> public void suspend() { }
> public void resume() { }
> public void beforeCommit(boolean readOnly)
> {
> getTransactionTemplate().execute(new
> TransactionCallbackWithoutResult() {
> protected void doInTransactionWithoutResult(final
> TransactionStatus status) {
> update("update m_t set trans_time=null where trans_id=?",
> new Object[] { transId });
> }
> });
> }
> public void beforeCompletion() { }
> public void afterCompletion(final int status) {
> getTransactionTemplate().execute(new
> TransactionCallbackWithoutResult() {
> protected void doInTransactionWithoutResult(final
> TransactionStatus ts) {
> if(status == STATUS_ROLLED_BACK) {
> update("update m_t set s_last=trans_s_start, trans_id=null,
> trans_time=null where trans_id=?",
> new Object[] { transId} );
> } else {
> update("update m_t set trans_id=null, trans_time=null,
> trans_s_start=null where trans_id=?",
> new Object[] { transId });
> }
> }
> });
> }
> });
> ---------------
>
> The TransactionTemplate returned from getTransactionTemplate() was created
> like this:
> transactionTemplate = new TransactionTemplate(transactionManager);
>
> transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATIO
>N_REQUIRES_NEW);
> transactionTemplate.setTimeout(getTransactionTimeoutInMs());
> transactionTemplate.setReadOnly(false);
>
>
> My question is, should I be able to do this? The exception is not thrown
> in many cases - however, in this particular case, an exception was thrown
> via other code in the context of the main transaction, though it did not
> cause a rollback to occur, so the transaction was in the process of being
> committed - and while committing the main transaction, the process of
> starting a new transaction caused this related exception to be thrown.
> This is against Spring 1.2.1.
>
> - Andy
>
> PS. Here is a full stack trace, for what its worth:
> java.lang.IllegalStateException: No value for key
> [org.hibernate.impl.SessionFactoryImpl@1f37bf1] bound to thread [Timer-3]
> at
> org.springframework.transaction.support.TransactionSynchronizationManager.u
>nbindResource(TransactionSynchronizationManager.java:175) at
> org.springframework.orm.hibernate3.SessionFactoryUtils$SpringSessionSynchro
>nization.suspend(SessionFactoryUtils.java:846) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>suspend(AbstractPlatformTransactionManager.java:350) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>getExistingTransaction(AbstractPlatformTransactionManager.java:271) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>getTransaction(AbstractPlatformTransactionManager.java:215) at
> org.springframework.transaction.support.TransactionTemplate.execute(Transac
>tionTemplate.java:111) at
> com.marathon.service.dao.jdbc.PSDAO$2.afterCompletion(PSDAO.java:185) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>triggerAfterCompletion(AbstractPlatformTransactionManager.java:610) at
> org.springframework.transaction.support.AbstractPlatformTransactionManager.
>commit(AbstractPlatformTransactionManager.java:467) at
> org.springframework.transaction.interceptor.TransactionAspectSupport.doClos
>eTransactionAfterThrowing(TransactionAspectSupport.java:294) at
> org.springframework.transaction.interceptor.TransactionInterceptor.invoke(T
>ransactionInterceptor.java:61) at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflec
>tiveMethodInvocation.java:144) at
> net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor
>.invoke(MethodSecurityInterceptor.java:80) at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflec
>tiveMethodInvocation.java:144) at
> org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopPr
>oxy.java:174) at $Proxy58.postApproved(Unknown Source)
>
>
> -------------------------------------------------------
> SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
> from IBM. Find simple to follow Roadmaps, straightforward articles,
> informative Webcasts and more! Get everything you need to get up to
> speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click
> _______________________________________________
> Springframework-developer mailing list
> Spr...@li...
> https://lists.sourceforge.net/lists/listinfo/springframework-developer
|