Pregunta

I am using Spring Data JPA (SDJ) and during my integration tests, I've come across a weird situation - I've probably misconfigured something, but I fail to see what that might be.

In a nutshell - it would seem that SDJ simply fails to invoke an implementation of Spring's PlatformTransactionManager once there is any kind of method declared on repository interface (i.e., the one that extends JpaRepository<T, ID>). I've drilled down this behaviour to a pretty banal reason - in this situation, JtaTransactionManager's setter simply does not invoke, but I couldn't figure out the reason.

Should interface lack any method declaration - everything works as is it should: the JtaTransactionManager setter invokes normally and transactions initiate, but I really dislike writing unneeded custom Repositories just to get around a possible misconfiguration/bug (and I have confirmed implementing a custom repository indeed circumvents mentioned behaviour).

I would appreciate any info you may give me as I am running out of ideas here.

This is my relevant stack:

  • JDK 1.5
  • Spring 3.1.1.RELEASE
  • Spring Data JPA 1.1 GA (issue confirmed on version 1.0.3)
  • Atomikos 3.7.0
  • OpenJPA 2.0.1
  • DB2 9.7

This rudimentary interface will execute normally:

package org.test
public interface TestDAO extends JpaRepository<Test, Integer> {}

The following repository interface will cause the exception below:

package org.test
public interface TestDAO extends JpaRepository<Test, Integer> {
//public static final String TEST = "SELECT t FROM Test t WHERE t.code=:code"; 

//@Query(TEST)
List<Test> findByCode(String code);}

Provoked exception (the exception is thrown during Spring application context bootstrap, in "configuration-time"):

08:23:40.296 | DEBUG | [o.s.d.r.c.AbstractRepositoryConfigDefinitionParser:110] | Triggering auto repository detection
08:23:40.997 | DEBUG | [o.s.d.r.c.AbstractRepositoryConfigDefinitionParser:233] | Registering repository: testDAO - Interface: org.test.TestDAO - Factory: org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean, - Custom implementation: null
.....
08:23:41.309 | DEBUG | [o.s.aop.framework.JdkDynamicAopProxy:113]         | Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [org.springframework.data.jpa.repository.support.SimpleJpaRepository@6d886d88]
08:23:41.338 | DEBUG | [o.s.d.j.r.query.SimpleJpaQuery:122]               | Looking up query for method findByCode
08:23:41.342 | DEBUG | [o.s.d.j.repository.query.NamedQuery:108]          | Looking up named query Test.findByCode
08:23:43.451 | TRACE | [openjpa.jdbc.SQL:83]                              | <t 16515324, conn 2070969200> executing prepstmnt 694298978 SELECT SEQSCHEMA AS SEQUENCE_SCHEMA, SEQNAME AS SEQUENCE_NAME FROM SYSCAT.SEQUENCES
08:23:43.551 | TRACE | [openjpa.jdbc.SQL:83]                              | <t 16515324, conn 2070969200> [99 ms] spent
08:24:07.248 | INFO  | [o.s.b.f.s.DefaultListableBeanFactory:433]         | Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6d966d96: defining beans [....]; root of factory hierarchy
08:24:07.253 | INFO  | [o.s.o.j.LocalContainerEntityManagerFactoryBean:441] | Closing JPA EntityManagerFactory for persistence unit 'xxx'
08:24:07.307 | ERROR | [o.s.test.context.TestContextManager:324]          | Caught exception while allowing TestExecutionListener [....] to prepare test instance [org.test.SomeIntegrationTest@64026402]
java.lang.NullPointerException: null
    at org.apache.openjpa.kernel.AbstractBrokerFactory.syncWithManagedTransaction(AbstractBrokerFactory.java:720) ~[openjpa-kernel-2.0.1.jar:2.0.1]
Wrapped by: org.apache.openjpa.persistence.PersistenceException: null
    at org.apache.openjpa.kernel.AbstractBrokerFactory.syncWithManagedTransaction(AbstractBrokerFactory.java:752) ~[openjpa-kernel-2.0.1.jar:2.0.1]
    at org.apache.openjpa.kernel.BrokerImpl.initialize(BrokerImpl.java:371) ~[openjpa-kernel-2.0.1.jar:2.0.1]
    at org.apache.openjpa.kernel.BrokerImpl.initialize(BrokerImpl.java:315) ~[openjpa-kernel-2.0.1.jar:2.0.1]
    at org.apache.openjpa.kernel.AbstractBrokerFactory.initializeBroker(AbstractBrokerFactory.java:231) ~[openjpa-kernel-2.0.1.jar:2.0.1]
    at org.apache.openjpa.kernel.AbstractBrokerFactory.newBroker(AbstractBrokerFactory.java:215) ~[openjpa-kernel-2.0.1.jar:2.0.1]
    at org.apache.openjpa.kernel.DelegatingBrokerFactory.newBroker(DelegatingBrokerFactory.java:156) ~[openjpa-kernel-2.0.1.jar:2.0.1]
    at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:227) ~[openjpa-persistence-2.0.1.jar:2.0.1]
    at org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:154) ~[openjpa-persistence-2.0.1.jar:2.0.1]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.5.0]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:79) ~[na:1.5.0]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.5.0]
    at java.lang.reflect.Method.invoke(Method.java:618) ~[na:1.5.0]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.invokeProxyMethod(AbstractEntityManagerFactoryBean.java:376) ~[spring-orm-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean$ManagedEntityManagerFactoryInvocationHandler.invoke(AbstractEntityManagerFactoryBean.java:517) ~[spring-orm-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at $Proxy24.createEntityManager(Unknown Source) ~[na:na]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:234) ~[spring-orm-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at $Proxy31.createNamedQuery(Unknown Source) ~[na:na]
    at org.springframework.data.jpa.repository.query.NamedQuery.hasNamedQuery(NamedQuery.java:90) ~[spring-data-jpa-1.1.0.RELEASE.jar:na]
    at org.springframework.data.jpa.repository.query.NamedQuery.<init>(NamedQuery.java:69) ~[spring-data-jpa-1.1.0.RELEASE.jar:na]
    at org.springframework.data.jpa.repository.query.NamedQuery.lookupFrom(NamedQuery.java:111) ~[spring-data-jpa-1.1.0.RELEASE.jar:na]
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:125) ~[spring-data-jpa-1.1.0.RELEASE.jar:na]
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:160) ~[spring-data-jpa-1.1.0.RELEASE.jar:na]
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:68) ~[spring-data-jpa-1.1.0.RELEASE.jar:na]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:280) ~[spring-data-commons-core-1.3.0.RELEASE.jar:na]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:148) ~[spring-data-commons-core-1.3.0.RELEASE.jar:na]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:125) ~[spring-data-commons-core-1.3.0.RELEASE.jar:na]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:41) ~[spring-data-commons-core-1.3.0.RELEASE.jar:na]
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142) ~[spring-beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]

When you delve into the code that bombs (OpenJPA AbstractBrokerFactory class), it follows that OpenJPA expects the given transaction manager (as defined in OpenJPA properties map below), but Spring doesn't provide it, since setter on JtaTransactionManager class is not invoked at all (I've verified this behaviour while debugging).

Contrary to that, if interface without any methods is invoked, the setter is called normally and provides transaction manager to OpenJPA.


Here's my configuration - one thing that you may notice and could potentially be relevant: I am not using persistence.xml file at all.

Datasource

<bean id="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
    <property name="uniqueResourceName" value="${dataSource.uniqueName}" />     
    <property name="xaDataSourceClassName" value="${dataSource.className}" />
    <property name="poolSize" value="100" />
    <property name="maxPoolSize" value="250" />
    <property name="borrowConnectionTimeout" value="5000" />
    <property name="xaProperties">
        <props>
        //snipped for brevity....
        </props>
    </property>
</bean>

Transaction manager

<!-- Construct Atomikos UserTransactionManager, needed to configure Spring -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
    <property name="forceShutdown" value="true" />
</bean>

<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
    <property name="transactionTimeout" value="3600"/>
</bean>

<!-- Configure the Spring framework to use JTA transactions from Atomikos -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManager" ref="atomikosTransactionManager" />
    <property name="userTransaction" ref="atomikosUserTransaction" />
    <property name="allowCustomIsolationLevels" value="true" />
</bean>

Entity manager

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="persistenceUnitName" value="${entityManagerFactory.persistenceUnitName}"/>
    <property name="packagesToScan" value="org.test.model" />
    <property name="jpaDialect" ref="jpaDialect"/>
    <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
    <property name="jpaProperties" ref="openjpaProperties" />
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.OpenJpaDialect"></bean> 
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter"></bean>

OpenJPA properties

<util:map id="openjpaProperties">
        <!-- Specifies whether JPA is using Managed (global) transactions or Local transactions -->
        <entry key="openjpa.TransactionMode" value="managed" />
        <entry key="openjpa.ConnectionFactoryMode" value="managed" />
        <entry key="openjpa.ConnectionFactory2" value-ref="dataSourceNonXA" />
        <!-- Specifies transaction manager to be used -->
        <entry key="openjpa.ManagedRuntime" value="invocation(TransactionManagerMethod=com.atomikos.icatch.jta.TransactionManagerImp.getTransactionManager)" />
</util:map>

JPA repository scanner

<jpa:repositories base-package="hr.apisit.b28.intrastat.business.storage.codelists.internal.dao" entity-manager-factory-ref="entityManagerFactory"
                transaction-manager-ref="transactionManager" />

Thanks in advance

¿Fue útil?

Solución

You need to init the atomikos transaction manager at startup. So, the only thing needed here is to add this:

<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close" lazy-init="false"> <property name="forceShutdown" value="true" /> </bean>

Otros consejos

you can try this:

<jpa:repositories base-         package="hr.apisit.b28.intrastat.business.storage.codelists.internal.dao" entity-manager-factory-ref="entityManagerFactory"
            transaction-manager-ref="transactionManager" depends-on="transactionManager"/>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top