0

I'm working on spring boot application, which already has a database connection established in its applicationContext.xml file and the necessary transaction manager and vendors etc.

I now need to connect the app to a second database. But I'm having issues with this. In my unit tests the connection is fine and can make simple queries to retrieve data, which is all I need it to do. However when I compile the app into a jar and run it, I get the following error

NoUniqueBeanDefinitionException: No qualifying bean of type "org.springframework.transaction.PlatformTransactionManager" available: expected single matching bean but found 2: transactionManager, transactionManager2

I have spent ages looking up how to solve this, and the suggested fixes I have found here , here and here have not worked.

I have one persistence.xml with two persistence units defined. And in my applicaitonContext.xml I defined two datasources, two transaction managers and two entity Manager Factories. I then use the @persitsencecontext and @Transactional("") annotations to say which persistence unit and managers to use, but I still get an error. I also added in the <qualifier> tag to the app context file, as I saw this as a suggested fix with the @transactional annotation, still no luck.

My code is below, can anyone spot an errors I have made, and why it may not be working as expected

applicationContext.xml

<bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="..."/>
    <property name="username" value="..."/>
    <property name="password" value="..."/>
</bean>


<bean id="entityManagerFactory" name="proxy">
    <property name="persistenceUnitName" value="proxy" />
    <property name="persistenceUnitXmlLocation" value="classpath:META-INF/persistence.xml" />
    <property name="dataSource" ref="dataSource1" />
    <property name="jpaVendorAdapter" ref="hiberanteVendorAdapter" />
    <property name="jpaProperties">
        <props>
            <prop key="hiberante.hbm2ddl.auto">valudate</prop>
        </props>
    </property>
</bean>

<bean id="hibernateVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" />
    <property name="database" value="HSQL" />
    <property name="showSql" value="true" />
</bean>

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



<!-- Second datasource -->
<bean id="dataSource2" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="..."/>
    <property name="username" value="..."/>
    <property name="password" value="..."/>
</bean>

<bean id="entityManagerFactory2" name="proxy">
    <property name="persistenceUnitName" value="proxy2" />
    <property name="persistenceUnitXmlLocation" value="classpath:META-INF/persistence.xml" />
    <property name="dataSource" ref="dataSource2" />
    <property name="jpaVendorAdapter" ref="hiberanteVendorAdapter2" />
    <property name="jpaProperties">
        <props>
            <prop key="hiberante.hbm2ddl.auto">valudate</prop>
        </props>
    </property>
</bean>


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

<bean id="hibernateVendorAdapter2" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />


<tx:annotation-driven/>

Implementation

@Repository
@Transactional("transactionManager2")
public class myDaoImpl extends GenericJPADao<Integer, Integer> implements ImyDao {

    @PersistenceContext(unitName="proxy2")
    protected EntityManager em;


}

SOLUTION

The accepted answer was the correct solution for me, but a few things to note. The beans have to point to their respective entityManagerFactory's and you need to be careful on which bean you set the autowire-candidate="false" on, as I set it on the incorrect one at first, and had transactions rolled back as a result. I think there could be cleaner solution to this, but as a quick fix it works fine

Eoin
  • 330
  • 1
  • 3
  • 15

1 Answers1

1

try this :

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" autowire-candidate="false">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
 </bean>


<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />    
</bean>
Arnab Dhar
  • 239
  • 2
  • 15
  • Does this need to be done on both transaction managers? Or just one? – Eoin Jun 14 '18 at 10:33
  • replace those two beans with mine..Make sure only "transactionManager" is autowire-candidate="false" – Arnab Dhar Jun 14 '18 at 10:35
  • Both those beans can't have the same right? I get a `Bean name 'transactionManager' is already used in this element` I assume one should be transactionManager1 and the other transactionManager2, and reference their specific factories? – Eoin Jun 14 '18 at 10:46
  • sorry, my bad, that should be transactionManager2 . Edited the answer. – Arnab Dhar Jun 14 '18 at 10:50
  • 1
    It looks like that has worked! I also had to make sure each bean referred to it's own entitymanagerfactory. You might want your answer to reflect that too. Could you explain what the `autowire-candidate="false"` does? And why it's only needed on one of the beans? – Eoin Jun 14 '18 at 12:32
  • 1
    From your error, NoUniqueBeanDefinitionException, I got that spring is confusing between your both transactionManager bean, Since you needed to do autowiring of transactionManager2 bean only, I suggested you to make one of them autowire-candidate="false". This tag restrict the bean to take part in autowiring and helps spring when these type of ambiguity comes up. If your liked my question, please accept it and vote up, so it will be helpful to others also. – Arnab Dhar Jun 14 '18 at 14:01
  • 1
    I have a follow up question to your explanation. While I do not need to autowire transactionManager1 for this part, there are other parts of the application where it does need to be autowried. Will this cause issues when those transactions need to be completed? Because i'm noticing other errors where other transasctions are being rolled back and not committed that use transactionManager1. I'm just wondering if this is the reason why – Eoin Jun 14 '18 at 15:50