PDA

View Full Version : Spring @Transactional not working as expected.



akeyla
Jun 30th, 2009, 09:37 AM
Hi,
We have legacy Hibernate 2.1 code wherein we used a custom "session per transaction" pattern. We are on Jboss 4.2.0 and the code works perfectly when used within EJBs. In the openSession() call, we go and get the associated transaction and the assign a SessionSynchronization object to the session. On commit, the session is flushed and closed, on rollback the session is closed without flush. Relevant code portions are attached


/** Get the current JTA transaction */
private static javax.transaction.Transaction getCurrentTransaction(SessionFactory sessionFactory)
throws SystemException
{
javax.transaction.Transaction trans = null;
SessionFactoryImplementor sessionFactoryImpl = (SessionFactoryImplementor)sessionFactory;
if (sessionFactoryImpl != null)
{
TransactionManager txManager = sessionFactoryImpl.getTransactionManager();

if (txManager != null)
{
trans = txManager.getTransaction();
}
}
return trans;
}

Once transaction has been obtained, register a synchronization object with the transaction. Note that the code associated with the association of session and transaction in a hashmap for future lookups of session based on transaction is excluded, the code below refers to when a new transaction is encountered.


Synchronization synch = new SessionSynchronization(domainObjectClass, t);
if (t != null)
{
t.registerSynchronization(synch);
map.put(t, s);
threadTxnSession.set(map);
}

This code works flawlessly in an EJB context. When a method is called (that has a transaction semantic), the synchronization object is created and when the method exits properly the transaction is committed and session is flushed and closed and if there is a RuntimeException, the transaction is rolled back and the session is cliosed without flushing.
The hibernate cfg.xml file has


<hibernate-configuration>

<!-- a SessionFactory instance listed as /jndi/name -->
<session-factory name="jdbc.datasource.MortracBravuraDataSource.hbm">

<!-- properties -->
<property name="connection.datasource">jdbc.datasource.MortracBravuraDataSource</property>
<property name="dialect">net.sf.hibernate.dialect.OracleDialect</property>
<property name="hibernate.transaction.factory_class">net.sf.hibernate.transaction.JTATransactionFactory</property>
<property name="jta.UserTransaction">UserTransaction</property>
<property name="transaction.manager_lookup_class">net.sf.hibernate.transaction.JBossTransactionManag erLookup</property>
<property name="use_outer_join">true</property>
<property name="show_sql">true</property>
<property name="format_sql">false</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="interceptor">com.fsb.framework.persistence.HibernateInterceptor</property>

<!-- mapping files -->

<mapping resource="com/fsb/domain/ajax/ZipCode.hbm.xml"/>
<mapping resource="com/fsb/domain/Country.hbm.xml"/>
<mapping resource="com/fsb/domain/HBM_IncomeDetail.hbm.xml"/>
<!-- OTHER MAPPINGS -->
</session-factory>
</hibernate-configuration>


Now we want to reuse the same Hibernate code within a war and rather than going with EJBs we are hoping to replicate the Transactional behaviour using Spring @Transactional. We are using Spring 2.5.1. The methods are annotated with @Transactional and in the applicationContext-jta.xml file, we have


<!-- We want to use Spring's declarative @Transaction management -->
<tx:annotation-driven transaction-manager="transactionManager"/>

<!-- Run Time Txn Manager Bean -->
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransaction Manager">
<property name="transactionManagerName" value="java:/TransactionManager"></property>
<property name="userTransactionName" value="java:comp/UserTransaction"></property>
</bean>


More info in next post.


Any help in this matter is appreciated. Our assumption is that we are missing some small thing. If you need any more information, I will be glad to provide it.

Regards
Sandesh

akeyla
Jun 30th, 2009, 09:42 AM
The log file shows the following (some parts cut out to post limit of 10000 chars):


29 Jun 2009 16:55:33,694 DEBUG Thrd-main JtaTransactionManager: Retrieving JTA UserTransaction from JNDI location [java:comp/UserTransaction]
29 Jun 2009 16:55:33,694 DEBUG Thrd-main JtaTransactionManager: Retrieving JTA TransactionManager from JNDI location [java:/TransactionManager]
29 Jun 2009 16:55:33,694 INFO Thrd-main JtaTransactionManager: Using JTA UserTransaction: org.jboss.tm.usertx.client.ServerVMClientUserTrans action@dc8de9
29 Jun 2009 16:55:33,694 INFO Thrd-main JtaTransactionManager: Using JTA TransactionManager: com.arjuna.ats.jbossatx.jta.TransactionManagerDele gate@1e8e471
29 Jun 2009 16:55:33,709 DEBUG Thrd-main JtaTransactionManager: JTA 1.1 [javax.transaction.TransactionSynchronizationRegist ry] not available
29 Jun 2009 16:55:37,980 DEBUG Thrd-main AnnotationTransactionAttributeSource: Adding transactional method [isAvailable] with attribute [PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,timeout _120,-PayoffRequestValidationException,-PayoffRequestException,-MortracSecurityException]
29 Jun 2009 16:55:38,011 DEBUG Thrd-main AnnotationAwareAspectJAutoProxyCreator: Creating implicit proxy for bean 'payoffRequestService' with 0 common interceptors and 1 specific interceptors
29 Jun 2009 16:55:38,027 DEBUG Thrd-main Cglib2AopProxy : Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [com.fsb.module.servicing.payoff.service.PayoffRequ estServiceImpl@1293ce0]
29 Jun 2009 16:55:38,681 DEBUG Thrd-main AnnotationTransactionAttributeSource: Adding transactional method [isAvailable] with attribute [PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,timeout _120,-PayoffRequestValidationException,-PayoffRequestException,-MortracSecurityException]
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: public boolean com.fsb.module.servicing.payoff.service.PayoffRequ estServiceImpl.isAvailable(java.lang.String,java.l ang.String) throws com.fsb.module.servicing.payoff.PayoffRequestValid ationException,com.fsb.module.servicing.payoff.Pay offRequestException,com.fsb.module.security.Mortra cSecurityException
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: public void com.fsb.module.servicing.payoff.service.PayoffRequ estServiceImpl.setMessageSource(org.springframewor k.context.MessageSource)
29 Jun 2009 16:55:38,681 DEBUG Thrd-main AnnotationTransactionAttributeSource: Adding transactional method [isActivePayoff] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,timeout_60,-PayoffRequestException]
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: public boolean com.fsb.module.servicing.payoff.service.PayoffRequ estServiceImpl.isActivePayoff(java.lang.String) throws com.fsb.module.servicing.payoff.PayoffRequestExcep tion
29 Jun 2009 16:55:38,681 DEBUG Thrd-main AnnotationTransactionAttributeSource: Adding transactional method [createPayoffRequest] with attribute [PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,timeout _120,-PayoffRequestValidationException,-PayoffRequestException]
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: public com.fsb.domain.mortgage.MortgageDAO com.fsb.module.servicing.payoff.service.PayoffRequ estServiceImpl.getMortgageDAO()
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Found 'hashCode' method: public native int java.lang.Object.hashCode()
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Found finalize() method - using NO_OVERRIDE
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Found 'equals' method: public boolean java.lang.Object.equals(java.lang.Object)
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: public java.lang.String java.lang.Object.toString()
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Method is declared on Advised interface: public abstract int org.springframework.aop.framework.Advised.indexOf( org.aopalliance.aop.Advice)
29 Jun 2009 16:55:38,681 DEBUG Thrd-main Cglib2AopProxy : Method is declared on Advised interface: public abstract java.lang.Class org.springframework.aop.TargetClassAware.getTarget Class()
29 Jun 2009 16:55:38,915 DEBUG Thrd-main AnnotationTransactionAttributeSource: Adding transactional method [validateRequest] with attribute [PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,timeout _120]
29 Jun 2009 16:55:38,915 DEBUG Thrd-main AnnotationAwareAspectJAutoProxyCreator: Creating implicit proxy for bean 'payoffRequestValidator' with 0 common interceptors and 1 specific interceptors
29 Jun 2009 16:55:38,915 DEBUG Thrd-main Cglib2AopProxy : Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [com.fsb.module.servicing.payoff.RequestValidator@2 dbfc1]
29 Jun 2009 16:55:38,915 DEBUG Thrd-main AnnotationTransactionAttributeSource: Adding transactional method [validateRequest] with attribute [PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,timeout _120]
29 Jun 2009 16:55:38,915 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: public void com.fsb.module.servicing.payoff.RequestValidator.v alidateRequest(com.fsb.module.payoff.service.PayOf fRequestType) throws com.fsb.module.servicing.payoff.exception.DataExce ption
29 Jun 2009 16:55:38,915 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: public void com.fsb.module.servicing.payoff.RequestValidator.s etMortgageDAO(com.fsb.domain.mortgage.MortgageLite DAO)
29 Jun 2009 16:55:38,915 DEBUG Thrd-main Cglib2AopProxy : Found 'hashCode' method: public native int java.lang.Object.hashCode()
29 Jun 2009 16:55:38,915 DEBUG Thrd-main Cglib2AopProxy : Found finalize() method - using NO_OVERRIDE
29 Jun 2009 16:55:38,915 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException
29 Jun 2009 16:55:38,915 DEBUG Thrd-main Cglib2AopProxy : Found 'equals' method: public boolean java.lang.Object.equals(java.lang.Object)
29 Jun 2009 16:55:38,915 DEBUG Thrd-main Cglib2AopProxy : Unable to apply any optimisations to advised method: public java.lang.String java.lang.Object.toString()



So it seems that the methods are recognized as being transactional methods BUT the transactions never get created. I have tried debugging the code but at the getCurrentTransaction() method, the transaction always shows up as null.

As a workaround we created a custom annotation


@Around("execution(* *.*(..)) && " +
"@annotation(com.fsb.module.servicing.CustomTransac tional)")
public Object runTransaction(ProceedingJoinPoint pjp) throws Throwable
{
LOG.debug("In runTransaction");
UserTransaction utx = null;
Object retval = null;

try
{
Context ctx = new InitialContext();
utx = getUserTransaction(ctx);

utx.begin();

try
{
retval = pjp.proceed();

LOG.debug("Committing");
utx.commit();
}
catch (Throwable th)
{
LOG.debug("Caught throwable", th);
if (th instanceof RuntimeException)
{
LOG.debug("Rollback after catching RTE");
utx.rollback();
}
else if (th instanceof Exception)
{
LOG.debug("Commit after catching Exception");
utx.commit();
}
throw th;
}
}

and this works as expected.

However, we do not want to replicate the @Transactional features in our custom annotation. We would rather that we get our code working with the Spring @Transactional annotation.

Any help is appreciated.

Regards
Sandesh