简体   繁体   中英

Spring + 2 Hibernate datasources + 2 TransactionManagers: NoSuchBeanDefinitionException

I have a Spring Web application with two Hibernate data sources, and they are being managed with two separate transaction managers. The datasources are completely independent, schema-wise. This configuration passes all unit tests and integration tests, but when I deploy it in Jetty, the repository operations fail with the below exception:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named my_transactionManager1' is defined: No unique PlatformTransactionManager bean found for qualifier 'my_transactionManager1'
at org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.qualifiedBeanOfType(BeanFactoryAnnotationUtils.java:84)
at org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.qualifiedBeanOfType(BeanFactoryAnnotationUtils.java:55)
at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:246)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:100)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:625)
at my.controller.Class$$EnhancerByCGLIB$$3976e5ef.myMethodCall(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

persistence.xml

<persistence-unit name="pu1">
<properties>
    <property name="hibernate.dialect" value="${hibernate.dialect}" />
    <property name="hibernate.connection.url" value="${network.db.url}" />
    <property name="hibernate.connection.driver_class" value="${hibernate.connection.driver_class}" />
    <property name="hibernate.connection.username" value="{db.username}" />
    <property name="hibernate.connection.password" value="{db.password}" />
    <property name="hibernate.enable_lazy_load_no_trans" value="true"/>
    <property name="hibernate.hbm2ddl.auto" value="${hibernate.hbm2ddl.auto}" />
    <property name="hibernate.show_sql" value="false" />
</properties>

Looking at the logs, both datasources seems to behave sanely (until this error occurs). Below is the application context:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/jdbc
    http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">



    <tx:annotation-driven transaction-manager="my_transactionManager1" />
    <context:component-scan base-package="com.my.package"/>
    <jpa:repositories
        base-package="com.my.package" entity-manager-factory-ref="my_entityManagerFactory" transaction-manager-ref="my_transactionManager1">
    </jpa:repositories>
    <bean id="my_dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="${my.db1.url}"/>
        <property name="username" value="${db1.username}"/>
        <property name="password" value="${db1.password}"/>
    </bean>

    <bean id="my_entityManagerFactory1"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="pu1" />
        <property name="dataSource" ref="my_dataSource1" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="databasePlatform" value="${hibernate.dialect}"/>
                <property name="generateDdl" value="false" />
                <property name="database" value="HSQL"/>
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
            </props>
        </property>
    </bean>

    <bean id="my_transactionManager1" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="my_entityManagerFactory1" />
        <property name="dataSource" ref="my_dataSource1" />
        <qualifier value="my_transactionManager1"/>
        <property name="persistenceUnitName" value="pu1"/>
    </bean>
</beans>

My Service Class where I'm trying to inject the TransactionManager:

Service
@Transactional(value="my_transactionManager1")
@PersistenceContext(unitName = "pu1", name="my_entityManagerFactory1")
public class MyServiceClass{

@Autowired
private Field myField

@Resource(name="my_transactionManager1")
private JpaTransactionManager my_transactionManager1;

/**
* Public no-arg constructor for bean initialization
*/
public MyServiceClass() {}

/**
* Protected IOC constructor for testing
*
* @param resultsService
*/
protected MyServiceClass(Field myField) {
this.myField = myField;
}

I have tried a lot of different approaches to work around this problem. One thing that is often suggested for this situation is a single JTA XA TransactionManager, but I would like to avoid that, at least for this first pass. Another thing that is suggested is using an AbstractDataRoutingSource, but I don't really want that either. The approach I have seems tenable (if not optimal), because tests are passing and the application deploys without error. Here is my web.xml (sorry for lengthy post):

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="site" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:my-first-context-file.xml,
            classpath:a-few-other-config-files.xml,
        </param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>


    <servlet>
        <servlet-name>MyServeletName</servlet-name>
        <servlet-class>
            com.sun.jersey.spi.spring.container.servlet.SpringServlet
        </servlet-class>
        <init-param>
            <param-name>
                com.sun.jersey.config.property.packages
            </param-name>
            <param-value>com.my.package</param-value>
        </init-param>
        <init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    </load-on-startup>
    </servlet>

    <!--More servlets -->

    <!-- Servlet mapping stuff -->


</web-app>

Any help at all would be much appreciated.

Make sure, you have unique ids of beans. It seems like you have 2 transactionManager beans with id="my_entityManagerFactory1" Any way, you should add to annotation propagation value: @Transactional(propagation=Propagation.REQUIRED, value="my_entityManagerFactory1")

Similar problem (one entity in 2 databases) I have solved by inheritence:

    @MappedSuperclass
public class User{
    private String name;
    private String surname;
    private String login;
    private String password;
}

@Entity
public class Employee extends User{
    //....
}
@Entity
public class CLient extends User{
    //...
}

Employee and Client classes were in different subpackages of entity package. One sessionFaction scanned first subpackage, other sessionFactory scanned second one. I had two services with @Transaction annotation like above and serched both databases by one manager class. On application I used only User entity

我通过将两个数据库合并为一个来“解决”该问题。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM