Spring无法在多个TransactionManager bean之间进行选择

时间:2014-09-05 11:13:12

标签: java spring jpa spring-boot spring-transactions

我将Spring批处理从命令行应用程序迁移到spring boot webapp,包括spring-batch-admin-manager(版本1.3.0)。

我的旧命令行应用程序使用了两个JPA数据库和两个TransactionManager实例。我记得,为了让它运行起来,这是一个很糟糕的配置。当我从Spring Boot开始时,我希望事情变得更加舒适,但现在情况似乎更糟:

Spring无法为事务选择正确的TransactionManager实例。

在应用程序启动时,Spring正在访问我的一个类来执行@PostConstruct带注释的代码块,从那里调用事务方法。

@PostConstruct
public void init() {
  eventType = eventTypeBo.findByLabel("Sport"); // <-- Calling transactional method
  //...
}

从这里抛出错误。看看stacktrace:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
           No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: 
           expected single matching bean but found 2: transactionManager,osm.transactionManager
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:313)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:337)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:252)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy89.findByLabel(Unknown Source)
    at com.company.model.service.impl.EventTypeBo.findByLabel(EventTypeBo.java:43)
    at com.company.batch.article.utils.converter.SomeConverter.init(SomeConverter.java:83)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    //...

从错误日志中可以看出,我的TransactionManager被命名为“transactionManager”和“osm.transactionManager”。交易配置相应:

<!-- DATABASE 1 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />

<!-- DATABASE 2 -->
<bean id="osm.transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="osm.entityManagerFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="osm.transactionManager">
    <tx:attributes>
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="osmServiceOperation"
        expression="execution(* com.qompa.osm.service.spec..*Service.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="osmServiceOperation" />
</aop:config>

我使用两种不同的GenericDao实现来区分访问数据时的PersistenceContext

public class OsmGenericDao<T> implements IGenericDao<T> {

  @PersistenceContext(unitName = "osm.entityManagerFactory")
  protected EntityManager em;
  //...
}

public class GenericDao<T> implements IGenericDao<T> {

  @PersistenceContext(unitName = "entityManagerFactory")
  protected EntityManager em;
  //...
}

似乎所有东西都配置得很好:但它仍然失败了!

编辑:据我所知,Spring TransactionAspectSuppor尝试通过限定符(determineTransactionManager)确定事务管理器。因为findByLabel的@Transactional注释没有限定符,所以它试图在DefaultListableBeanFactory的getBean方法的帮助下确定正确的bean,其中找到了两个相同类型的bean,无法进一步区分。 / p>

必须有一种方法告诉Spring根据持久化上下文选择transactionManager!

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

这是我的工作配置,包含2个持久化上下文:

<!-- METADATA -->

<!-- Metadata connection pool -->
<bean id="scprMetadataDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close">
    <property name="driverClass"
        value="..." />
    <property name="jdbcUrl"
        value="..." />
    <property name="user"
        value="..." />
    <property name="password"
        value="..." />
    <property name="maxPoolSize"
        value="..." />
    <property name="minPoolSize"
        value="..." />
</bean>

<!-- Metadata entity manager factory -->
<bean id="scprMetadataEntityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
            <property name="database" value="H2" />
        </bean>
    </property>
</bean>

<!-- Metadata transaction manager -->
<bean id="scprMetadataTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="scprMetadataEntityManagerFactory" />
</bean>


<!-- DOMAIN -->

<!-- Domain connection pool -->
<bean id="scprDomainDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close">
    <property name="driverClass"
        value="..." />
    <property name="jdbcUrl"
        value="..." />
    <property name="user"
        value="..." />
    <property name="password"
        value="..." />
    <property name="maxPoolSize"
        value="..." />
    <property name="minPoolSize"
        value="..." />
</bean>

<!-- Domain entity manager factory -->
<bean id="scprDomainEntityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
            <property name="database" value="SQL_SERVER" />
        </bean>
    </property>
</bean>

<!-- Domain transaction manager -->
<bean id="scprDomainTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="scprDomainEntityManagerFactory" />
</bean>

其他配置(在你的问题中遗漏):

<persistence-unit name="scpr_metadata" transaction-type="RESOURCE_LOCAL">
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <class>...</class>
    <class>...</class>
    <class>...</class>
</persistence-unit>

<persistence-unit name="scpr_domain" transaction-type="RESOURCE_LOCAL">
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <class>...</class>
    <class>...</class>
    <class>...</class>
</persistence-unit>

在我的java类中:

@Repository
public final class MetadataRepositoryImpl implements MetadataRepository {

@PersistenceContext(unitName = "scpr_metadata")
private EntityManager em;

@Repository
public final class DomainRepositoryImpl implements DomainRepository {

@PersistenceContext(unitName = "scpr_domain")
private EntityManager em;

希望这会对你有所帮助。