Atomikos Forum

JPA EntityManager not participating in JTA transaction

Hi,

I've introduced ActiveMQ into my Spring application and needed to move to using JTA instead of resource local JPA transactions as JMS messages were being sent and received before the database transaction had finished.

I've used Atomikos as the JTA provider and Spring appears to control the JTA transactions and JMS takes part but the database code doesn't. No modifications I make to entities are persisted and calling EntityManager.flush causes this exception:

--
javax.persistence.TransactionRequiredException:
Exception Description: No transaction is currently active
    org.eclipse.persistence.internal.jpa.transaction.EntityTransactionWrapper.throwCheckTransactionFailedException(EntityTransactionWrapper.java:113)
--

The Atomikos logging shows the global transactions being started and committed OK.

My relevant config is below:

In global application context:

--
    <bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="persistenceUnitName" value="PORTAL2DB" />
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
         </property>
          <property name="persistenceUnitPostProcessors" ref="persistenceUnitPostProcessor"/>
    </bean>

    <bean id="persistenceUnitPostProcessor" class="com.example.PersistentUnitProcessor">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource"
        class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean">
        <property name="uniqueResourceName" value="NONXADBMS" />
        <property name="user" value="${db.user}" />
        <property name="password" value="${db.password}"/>
        <property name="url" value="${db.url}"/>
        <property name="driverClassName" value="${db.driver}" />
        <property name="poolSize" value="100" />
        <property name="borrowConnectionTimeout" value="120" />
        <property name="testQuery" value="SELECT 1" />
        <property name="reapTimeout" value="0" />
    </bean>
    
    <bean id="AtomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> 
        <property name="forceShutdown" value="false" />
    </bean>
 
    <bean id="AtomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
        <property name="transactionTimeout" value="300" />
    </bean>
 
    <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="AtomikosTransactionManager" />
        <property name="userTransaction" ref="AtomikosUserTransaction" />
    </bean>

    <bean id="jmsFactory" class="org.apache.activemq.spring.ActiveMQXAConnectionFactory">
        <property name="brokerURL">
            <value>failover:tcp://localhost:61616</value>
        </property>
    </bean>

    <bean id="jmsConnectionFactoryBean" 
          class="com.atomikos.jms.AtomikosConnectionFactoryBean" 
          init-method="init" destroy-method="close">
        <property name="uniqueResourceName">
            <value>JMSCFB</value>
        </property>
        <property name="xaConnectionFactory">
            <ref bean="jmsFactory"/>
        </property>
        </bean>
   
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory">
            <ref local="jmsConnectionFactoryBean" />
        </property>
        <property name="receiveTimeout" value="1000"/>
        <property name="sessionTransacted" value="true"/>
    </bean>
--

and in the spring context:

--
  <aop:config proxy-target-class="true">
  <aop:pointcut id="txOperationController" expression="execution(* com.example.controller.*.*(..))"/>
  <aop:pointcut id="txOperationSecurity" expression="execution(* com.example.security.*.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="txOperationController"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="txOperationDnsController"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="txOperationSecurity"/>
  </aop:config>

  <tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
    <!-- all methods starting with 'get' are read-only -->
    <tx:method name="get*" read-only="true"/>
    <tx:method name="*" read-only="false"
        no-rollback-for="org.springframework.security.core.AuthenticationException"
      />
  </tx:attributes>
  </tx:advice>
--

The persistance unit is defined like this (yes I know it's not defined as JTA, see below):

--
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="PORTAL2DB">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>....</class>
        <properties>
            <property name="eclipselink.cache-usage" value="DoNotCheckCache"/>
            <property name="eclipselink.query-results-cache" value="false"/>
            <property name="eclipselink.maintain-cache" value="false"/>
            <property name="eclipselink.cache.shared.default" value="false"/>
            <property name="eclipselink.jdbc.bind-parameters" value="true"/>
            <property name="eclipselink.jdbc.cache-statements" value="true"/>
            <property name="eclipselink.weaving.internal" value="false"/>
        </properties>
    </persistence-unit>
</persistence>
--

The eagle eyed among may have spotted that my entity manager factory uses a persistence unit post processor. To make it JTA when used in Spring (it's used elsewhere) and to inject the JTA data source without messing with JNDI the post processor works as follows:

--
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Required;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;

public class PersistentUnitProcessor implements PersistenceUnitPostProcessor
{
    private DataSource dataSource;

    public void postProcessPersistenceUnitInfo(
            MutablePersistenceUnitInfo mutablePersistenceUnitInfo)
    {
        mutablePersistenceUnitInfo.setJtaDataSource(dataSource);
        mutablePersistenceUnitInfo.setTransactionType(PersistenceUnitTransactionType.JTA);
        System.out.println("Setup PU " + mutablePersistenceUnitInfo.getPersistenceUnitName() + " to be JTA");
    }

    @Required
    public void setDataSource(DataSource dataSource)
    {
        this.dataSource = dataSource;
    }
}
--

I see the above log message in the logs so I know that's being executed on startup.

I'm using the non XA datasource as I'm using MySQL Cluster and that doesn't support XA yet.

Thanks for any help. I'm off on holiday tomorrow so I'll take a couple of weeks to respond to replies!

Peter Spikings.
Peter Spikings Send private email
Thursday, July 25, 2013
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics