Quantcast
Channel: Spring Community Forums - Data
Viewing all articles
Browse latest Browse all 297

JtaTransactionManager no rollback after failed commit during "flush"

$
0
0
Update: I was testing it with Bitronix TM and it rollbacks perfectly, so the issue is in JBoss TM (arjuna) or in my configuration.
Update 2 : It looks like transactions are not global, I've tried different datasources, Bitronix datasource has *allowLocalTransactions* property and after setting it my application throws an exception that something tried to use it in local mode. If I use Bitronix with this datasource it works without any errors. I believe there is something wrong in configs.

Hello.
I have an issue with JTA transactions. I'm using Tomcat 7 + Hibernate 4 + Spring 3 + JBoss TS 4 and JTA transactions.

Suppose there is the following method:
@Transactional(propagation = Propagation.REQUIRED)
public void testMethod() {
insertOfSomeNewEntityInstance();
updateOfAnotherEntity();
}

If this method throws "org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)" during "updateOfAnotherEntity()" method execution or any other runtime exception that might happen during "flush" (Hibernate also shows: HHH000346: Error during managed flush [Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect).
then the result of insertOfSomeNewEntityInstance() execution is not rolled back.

After debugging this issue I found "doCommit" method in org.springframework.transaction.jta.JtaTransaction Manager
Code:

@Override
        protected void doCommit(DefaultTransactionStatus status) {
                JtaTransactionObject txObject = (JtaTransactionObject) status.getTransaction();
                try {
                        int jtaStatus = txObject.getUserTransaction().getStatus();
                        if (jtaStatus == Status.STATUS_NO_TRANSACTION) {
                                throw new UnexpectedRollbackException("JTA transaction already completed - probably rolled back");
                        }
                        if (jtaStatus == Status.STATUS_ROLLEDBACK) {
                                try {
                                        txObject.getUserTransaction().rollback();
                                }
                                catch (IllegalStateException ex) {
                                        if (logger.isDebugEnabled()) {
                                                logger.debug("Rollback failure with transaction already marked as rolled back: " + ex);
                                        }
                                }
                                throw new UnexpectedRollbackException("JTA transaction already rolled back (probably due to a timeout)");
                        }
                        txObject.getUserTransaction().commit();
                }
                catch (RollbackException ex) {
                        throw new UnexpectedRollbackException(
                                        "JTA transaction unexpectedly rolled back (maybe due to a timeout)", ex);
                }
                catch (HeuristicMixedException ex) {
                        throw new HeuristicCompletionException(HeuristicCompletionException.STATE_MIXED, ex);
                }
                catch (HeuristicRollbackException ex) {
                        throw new HeuristicCompletionException(HeuristicCompletionException.STATE_ROLLED_BACK, ex);
                }
                catch (IllegalStateException ex) {
                        throw new TransactionSystemException("Unexpected internal transaction state", ex);
                }
                catch (SystemException ex) {
                        throw new TransactionSystemException("JTA failure on commit", ex);
                }
        }

If "txObject.getUserTransaction().commit();" fails with RollbackException then this method throws UnexpectedRollbackException and here is the part of org.springframework.transaction.support.AbstractPl atformTransactionManager processCommit(...) that catches it:
Code:

}
 catch (UnexpectedRollbackException ex) {
                                // can only be caused by doCommit
                                triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
                                throw ex;
        }

I do not see any rollbacks in triggerAfterCompletion() method and after this method everything else just cleans up resources.

To sum up, spring/jboss just commits the result of insertOfSomeNewEntityInstance(), fails to execute updateOfAnotherEntity() because of concurrent modification error, and does not rollback anything. If I manually throw any runtime or checked exception from updateOfAnotherEntity() it rollbacks correctly, the issue occurs only when Hibernate throws some runtime exception during "flush".

hibernate.cfg
Code:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
        <property name="dialect">${dialect}</property>
        <property name="max_fetch_depth">1</property>
        <property name="hibernate.jdbc.batch_size">25</property>
        <property name="show_sql">false</property>
        <property name="format_sql">false</property>
        <property name="use_sql_comments">false</property>
    <property name="hibernate.session_factory_name">TestSessionFactory</property>
    <property name="hibernate.session_factory_name_is_jndi">false</property>
    <property name="hibernate.current_session_context_class">jta</property>
    <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
    <property name="hibernate.transaction.jta.platform">org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform</property>
    <property name="hibernate.id.new_generator_mappings">true</property>
        <property name="hibernate.cache.infinispan.cfg">infinispan.xml</property>
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.infinispan.InfinispanRegionFactory</property>
</session-factory>
</hibernate-configuration>

jbossts-properties.xml
Code:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <entry key="CoordinatorEnvironmentBean.commitOnePhase">YES</entry>
    <entry key="CoordinatorEnvironmentBean.defaultTimeout">300</entry>
    <entry key="ObjectStoreEnvironmentBean.transactionSync">ON</entry>
    <entry key="CoreEnvironmentBean.nodeIdentifier">1</entry>
        <entry key="JTAEnvironmentBean.xaRecoveryNodes">1</entry>
    <entry key="JTAEnvironmentBean.xaResourceOrphanFilterClassNames">
        com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter
        com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter
    </entry>

    <entry key="CoreEnvironmentBean.socketProcessIdPort">0</entry>

    <entry key="RecoveryEnvironmentBean.recoveryModuleClassNames">
        com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule
        com.arjuna.ats.internal.txoj.recovery.TORecoveryModule
        com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule
    </entry>

    <entry key="RecoveryEnvironmentBean.expiryScannerClassNames">
        com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner
    </entry>

    <entry key="RecoveryEnvironmentBean.recoveryPort">4712</entry>

    <entry key="RecoveryEnvironmentBean.recoveryAddress"></entry>

    <entry key="RecoveryEnvironmentBean.transactionStatusManagerPort">0</entry>
    <entry key="RecoveryEnvironmentBean.transactionStatusManagerAddress"></entry>

    <entry key="RecoveryEnvironmentBean.recoveryListener">YES</entry>

</properties>

Part of applicationContext.xml

Code:


<bean class="com.arjuna.ats.jta.TransactionManager" factory-method="transactionManager" id="arjunaTransactionManager"></bean>
    <bean class="com.arjuna.ats.jta.UserTransaction" factory-method="userTransaction" id="arjunaUserTransaction"></bean>

    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" >
        <property name="transactionManager" ref="arjunaTransactionManager"/>
        <property name="userTransaction" ref="arjunaUserTransaction"/>
    </bean>

    <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.XADataSource" destroy-method="close">
        <property name="url" value="${database.url}" />
        <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
        <property name="username" value="${database.user}" />
        <property name="password" value="${database.password}" />
    </bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" destroy-method="destroy">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation"><value>classpath:hibernate.cfg.xml</value></property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.cache.use_query_cache">false</prop>
            </props>
        </property>
    </bean>

    <tx:annotation-driven mode="proxy" proxy-target-class="false" transaction-manager="transactionManager"/>

I've tried everything, nothing helps :(

Viewing all articles
Browse latest Browse all 297

Trending Articles