4

我正在使用 Spring Data JPA (SDJ),在我的集成测试期间,我遇到了一个奇怪的情况——我可能错误配置了一些东西,但我看不到那可能是什么。

PlatformTransactionManager简而言之 -一旦在存储库接口上声明了任何类型的方法(即扩展的方法) ,SDJ 似乎根本无法调用 Spring 的实现JpaRepository<T, ID>。我已经将这种行为深入到一个非常平庸的原因 - 在这种情况下,JtaTransactionManager's setter 根本不会调用,但我无法弄清楚原因。

如果接口缺少任何方法声明 - 一切都按原样工作:JtaTransactionManagersetter 正常调用并启动事务,但我 真的不喜欢编写不需要的自定义存储库只是为了解决可能的错误配置/错误(我已经确认实现自定义存储库确实可以规避提到的行为)。

我会很感激你给我的任何信息,因为我在这里没有想法了。

这是我的相关堆栈:

  • JDK 1.5
  • Spring 3.1.1.RELEASE
  • Spring Data JPA 1.1 GA(已在 1.0.3 版确认问题)
  • Atomikos 3.7.0
  • OpenJPA 2.0.1
  • DB2 9.7

这个基本接口将正常执行:

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

以下存储库接口将导致以下异常:

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);}

引发的异常(在 Spring 应用程序上下文引导期间抛出异常,在“配置时间”):

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

4

2 回答 2

2

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>

于 2012-10-14T07:47:41.170 回答
0

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"/>
于 2012-05-31T17:58:06.310 回答