NoUniqueBeanDefinitionException when using multiple transaction managers in Spring Integration

Viewed 1541

Am using Spring Integration XML configuration for polling some database tables and then doing some processing. Everything was working fine, until we introduced multiple data sources. After that we are getting intermittent NoUniqueBeanDefinitionException exceptions.

Problem is we dont know from which poller is this exception coming (we are using xml based configuration), since we have a lot of pollers. Since the stack trace shows the AbstractPollingEndpoint.java, we looked at all the pollers and verified that the transaction-manager is set properly.

Code:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
        <property name="dataSource" ref="dataSource" />
</bean>

<bean id="transactionManagerSec" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactorySecondary"/>
        <property name="dataSource" ref="dataSourceSec" />
</bean>

<jpa:repositories base-package="com.acme.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" />
<jpa:repositories base-package="com.acme.tmpdao" entity-manager-factory-ref="entityManagerFactorySecondary" transaction-manager-ref="transactionManagerSec" />

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" primary="true" id="entityManagerFactory">
        <property name="dataSource" ref="dataSource"/>
        <property name="persistenceUnitName" value="${spring.persistence.unitname}" />
        <property name="persistenceProvider" ref="eclipsePP" />
</bean>

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" primary="false" id="entityManagerFactorySecondary">
        <property name="dataSource" ref="dataSourceSec"/>
        <property name="persistenceUnitName" value="${spring.secondary.persistence.unitname}" />
        <property name="persistenceProvider" ref="eclipsePPSec" />
</bean>

<bean class="org.eclipse.persistence.jpa.PersistenceProvider" id="eclipsePP" />
<bean class="org.eclipse.persistence.jpa.PersistenceProvider" id="eclipsePPSec" />

<int:transaction-synchronization-factory id="txSyncFactory">
        <int:before-commit channel="updateEntityChannel" />
</int:transaction-synchronization-factory>

<int-jpa:inbound-channel-adapter id="procStateAdapter"  channel="inProcStateChannel"
                    entity-manager="entityManagerFactory"
                    auto-startup="true"
                    jpa-query="select <SqlQuery> order by en.lastUpdatedTime"
                    max-results="10"
                    >
        <int:poller max-messages-per-poll="10" fixed-rate="10" >
            <int:transactional propagation="REQUIRES_NEW" transaction-manager="transactionManager" synchronization-factory="txSyncFactory"/>
        </int:poller>
    </int-jpa:inbound-channel-adapter>
2019-10-13 07:51:16,625 [task-scheduler-5] ERROR o.s.i.h.LoggingHandler:handleMessageInternal:192 - org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: transactionManager,transactionManagerSec
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:368)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:366)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:270)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
        at com.sun.proxy.$Proxy409.toString(Unknown Source)
        at java.lang.String.valueOf(String.java:2994)
        at java.lang.StringBuilder.append(StringBuilder.java:131)
        at org.springframework.integration.endpoint.PollingConsumer.handleMessage(PollingConsumer.java:141)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:272)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint.access$000(AbstractPollingEndpoint.java:58)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:190)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:186)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller$1.run(AbstractPollingEndpoint.java:353)
        at org.springframework.integration.util.ErrorHandlingTaskExecutor$1.run(ErrorHandlingTaskExecutor.java:55)
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
        at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:51)
        at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:344)
        at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
        at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

Is there any way to find out the id of the poller that is at the beginning of the AbstractPollingEndpoint$Poller$1.run from the stack below so we can debug this further?

Or anything else that is missing from our config for multiple data source.

1 Answers

In your stacktrace it can be seen that :

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected 
single matching bean but found 2: transactionManager,transactionManagerSec

So the issue is that spring finds two qualifying beans for autowiring. Try setting one of your bean as primary using the attribute as @Shailesh suggested :

<bean primary="true|false"/>

or use a qualifier to set the two transaction beans apart and then specify which transaction manager to be used in code like :

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
        <property name="dataSource" ref="dataSource" />
        <qualifier value="transactionManagerOne"/>
</bean>

<bean id="transactionManagerSec" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactorySecondary"/>
        <property name="dataSource" ref="dataSourceSec" />
        <qualifier value="transactionManagerSecond"/>
</bean>

and when you want to use a particular transaction manager specify it using the annotation like :

@Transactional("transactionManagerOne")

Note: You could configure this via xml as well

You can use the value attribute of the @Transactional annotation to optionally specify the identity of the PlatformTransactionManager to be used. This can either be the bean name or the qualifier value of the transaction manager bean.The default target bean name, transactionManager, is still used if no specifically qualified PlatformTransactionManager bean is found.

Official Doc

Related