【问题标题】:Using Multiple Entity managers in DAO在 DAO 中使用多个实体管理器
【发布时间】:2014-01-28 13:19:03
【问题描述】:

我正在尝试配置 Spring+Hibernate+JPA 以使用两个数据库(一个仅用于写入,即插入和更新,其他仅用于检索。

我做了一些研究并找到了这些可能的解决方案:

但我卡在一个地方并收到此错误

No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: entityManagerFactoryReadOnly,entityManagerFactoryWriteOnly

我做错了什么?

persistent.read.only.xml:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="readOnly" transaction-type="RESOURCE_LOCAL">        
        <provider>org.hibernate.ejb.HibernatePersistence</provider>     
        <class>com.demo.domain.Contact</class>  
    </persistence-unit>
</persistence>

persistent.write.only.xml:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="writeOnly" transaction-type="RESOURCE_LOCAL">       
        <provider>org.hibernate.ejb.HibernatePersistence</provider>     
        <class>com.demo.domain.Contact</class>  
    </persistence-unit>
</persistence>

mcv-dispatcher-servlet.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:lang="http://www.springframework.org/schema/lang" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
    xmlns:oxm="http://www.springframework.org/schema/oxm"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.2.xsd">

    <!-- Activates various annotations to be detected in bean classes -->
    <context:annotation-config />

    <!-- Scans the classpath for annotated components that will be auto-registered 
        as Spring beans. For example @Controller and @Service. Make sure to set the 
        correct base-package -->
    <context:component-scan base-package="com.demo" />

    <!-- Setup a simple strategy: 1. Take all the defaults. 2. Return XML by 
        default when not sure. -->
    <!-- Total customization - see below for explanation. -->
    <bean id="cnManager"
        class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <property name="favorPathExtension" value="true" />
        <property name="ignoreAcceptHeader" value="true" />
        <property name="defaultContentType" value="application/json" />
        <property name="useJaf" value="false" />

        <property name="mediaTypes">
            <map>
                <entry key="json" value="application/json" />
                <entry key="xml" value="application/xml" />
            </map>
        </property>
    </bean>

    <!-- Make this available across all of Spring MVC -->
    <mvc:annotation-driven
        content-negotiation-manager="cnManager" />

    <bean class="com.demo.view.MvcConfiguringPostProcessor" />

    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
        p:location="/WEB-INF/jdbc.properties" />


    <!-- ******************************************************************** -->
    <!--       START: Multiple C3P0 data-sources for DB instance                -->
    <!-- ******************************************************************** -->

    <!-- https://stackoverflow.com/questions/12922351/can-i-use-multiple-c3p0-datasources-for-db-instance -->
    <!-- Using Apache DBCP Data Sources -->
    <bean id="dataSource" 
        abstract="true" >
        <property name="driverClass" value="${db.driverClassName}" />
        <property name="user" value="${db.username}" />
        <property name="password" value="${db.password}" />
        <property name="idleConnectionTestPeriod" value="${db.idleConnectionTestPeriod}" />
        <property name="preferredTestQuery" value="select 1" />
        <property name="testConnectionOnCheckin" value="true" />
    </bean>

    <bean id="dataSourceReadOnly" 
        parent="dataSource"
        class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="jdbcUrl" value="${db.readOnlyDataBaseUrl}" />
    </bean>
    <bean id="dataSourceWriteOnly" 
        parent="dataSource"
        class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="jdbcUrl" value="${db.writeOnlyDataBaseUrl}" />
    </bean>

    <!-- ******************************************************************** -->
    <!--       END: Multiple C3P0 data-sources for DB instance              -->
    <!-- ******************************************************************** -->


    <bean id="jpaVendorProvider"
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="database" value="MYSQL" />
        <property name="databasePlatform" value="${db.dialect}" />
        <property name="showSql" value="true" />
        <property name="generateDdl" value="true" />
    </bean>

<!--    <bean -->
<!--        class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"> -->
<!--        <property name="defaultPersistenceUnitName" value="readOnly" /> -->
<!--     </bean> -->

    <bean id="persistenceUnitManager"
        class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
        <!-- defining multiple persistence unit -->
        <property name="persistenceXmlLocations">
            <list>
                <value>/META-INF/persistence.read.only.xml</value>
                <value>/META-INF/persistence.write.only.xml</value>
            </list>
        </property>
        <property name="defaultDataSource" ref="dataSourceReadOnly" /> 
        <property name="dataSources">
            <map>
                <entry key="readOnlyDsKey" value-ref="dataSourceReadOnly" />
                <entry key="writeOnlyDsKey" value-ref="dataSourceWriteOnly" />
            </map>
        </property>
    </bean>

    <bean id="entityManagerFactoryReadOnly"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--        <property name="dataSource" ref="dataSourceReadOnly" />      -->
        <property name="persistenceUnitManager" ref="persistenceUnitManager" />
        <property name="jpaVendorAdapter" ref="jpaVendorProvider" />
        <property name="persistenceUnitName" value="readOnly" />
        <!-- entityManagerFactory does not specify persistenceUnitName property 
            because we're defining more than one persistence unit -->
        <!-- <property name="persistenceUnitName" value="hello_mysql" /> -->
        <!-- <property name="persistenceXmlLocation" value="/META-INF/persistence.xml" /> -->
    </bean>

    <bean id="entityManagerFactoryWriteOnly"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--        <property name="dataSource" ref="dataSourceWriteOnly" />         -->
        <property name="persistenceUnitManager" ref="persistenceUnitManager" />
        <property name="jpaVendorAdapter" ref="jpaVendorProvider" />
        <property name="persistenceUnitName" value="writeOnly" />
    </bean>

    <!-- ******************************************************************** -->
    <!-- Mark bean transactions as annotation driven -->
    <!-- ******************************************************************** -->
    <tx:annotation-driven transaction-manager="transactionManagerReadOnly" />
    <tx:annotation-driven transaction-manager="transactionManagerWriteOnly" />

    <!-- ******************************************************************** -->
    <!-- Setup the transaction manager -->
    <!-- ******************************************************************** -->

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

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


</beans>

我的 DAO:

package com.demo.dao;

import java.util.Collections;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.demo.domain.Contact;

//import java.util.Collections;

@Repository("ContactDAO")
@Transactional
public class ContactDAOImpl extends AppDAOimpl<Contact> implements ContactDAO {

    /**
     * 
     */
    private static final long serialVersionUID = 3986253823316728444L;

    /**
     * EntityManager injected by Spring for persistence unit MYSQL
     * 
     */
    @PersistenceContext(unitName = "readOnly")
    @Qualifier("entityManagerFactoryReadOnly")
    private EntityManager entityManager;

    /**
     * Get the entity manager that manages persistence unit MYSQL
     * 
     */
    public EntityManager getEntityManager() {
        return entityManager;
    }

    /**
     * EntityManager injected by Spring for persistence unit MYSQL
     * 
     */
    @PersistenceContext(unitName = "writeOnly")
    @Qualifier("entityManagerFactoryWriteOnly")
    private EntityManager woEntityManager;

    /**
     * Get the entity manager that manages persistence unit MYSQL
     * 
     */
    public EntityManager getWoEntityManager() {
        return woEntityManager;
    }


    // other functions goes here 
}

两个数据库具有相同的架构(读取和写入)。

【问题讨论】:

  • 你能展示实体的外观吗?您是否在实体之上添加了架构声明?
  • 我没有在我的实体类顶部添加任何架构。

标签: java hibernate spring-mvc jpa


【解决方案1】:

我们在这里的一个项目中有类似的设置,我认为

@PersistenceContext(unitName = "writeOnly")
private EntityManager woEntityManager;

就足够了,你不需要额外的限定符。但根据我的经验,您也必须在 Transactional 上设置属性。所以删除 DAO 类上的 Transactional 注释并开始用

标记单个方法
@Transactional(value="transactionManagerReadOnly")

我相信上下文中的 tx:annotation-driven 元素也不适用于多个上下文。

理想情况下,无论如何,整个东西都属于服务层,您不希望您的 DAO 决定甚至知道从哪个 Persistence 上下文中调用它们。所以你会有一个 ReadContactService:

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

 @Transactional(value="transactionManagerReadOnly")
 public Contact readContact(int id) {
   return dao.findById(em, id);
 }

还有一个 WriteContactService:

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

 @Transactional(value="transactionManagerWriteOnly")
 public void writeContact(String name, String address) {
   return dao.writeContact(em, name, address);
 }

还有一个不知道上下文的 DAO。然后你只需要 N 个实体类就可以重用 DAO 方法(即使 writeOnly 最终也必须从数据库中读取,相信我)。

【讨论】:

  • 在服务层而不是 DAO 中使用持久单元/实体管理器是一种好习惯吗?
  • @Zeus:我已经尝试过你的解决方案,它仍然给我同样的错误。我找到了这个解决方案,但无法得到它(关于配置的观点)stackoverflow.com/questions/1961566/…
  • 例如,如果您想通过组合 DAO 创建交易,它会有所帮助。如果 DAO 自己处理他们的交易,你就必须想办法诱使他们不这样做。将事务层与实际数据访问分开可以解决这个问题。在您的情况下,对于两个数据库,您可能需要实现一个承载 unitName 和事务值的层。该层必须实现两次 afaik,因此应该很薄。
【解决方案2】:
JTA transaction manager. is answer to my question. Below are links for references.

& 这里是关于将 JTA 与 spring 集成的很好的教程。

http://www.javacodegeeks.com/2013/07/spring-jta-multiple-resource-transactions-in-tomcat-with-atomikos-example.html

【讨论】:

    【解决方案3】:

    添加两个类

    ContactWrite.java 在顶部声明架构和表,如下所示

    @Table(name = "联系人", schema="DB1")

    对 DB2 中的另一个表执行相同操作

    ContactRead.java

    @Table(name = "联系人", schema="DB2")

    现在在持久性 xml 文件中使用这两个类,如下所示。

    persistent.read.only.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <persistence version="2.0"
        xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    
        <persistence-unit name="readOnly" transaction-type="RESOURCE_LOCAL">        
            <provider>org.hibernate.ejb.HibernatePersistence</provider>     
            <class>com.demo.domain.ContactWrite</class>  
        </persistence-unit>
    </persistence>
    

    persistent.write.only.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <persistence version="2.0"
        xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    
        <persistence-unit name="writeOnly" transaction-type="RESOURCE_LOCAL">       
            <provider>org.hibernate.ejb.HibernatePersistence</provider>     
            <class>com.demo.domain.ContactRead</class>  
        </persistence-unit>
    </persistence>
    

    【讨论】:

    • 根据您的解决方案,如果我有“N”个实体,那么将有 2N 个实体类。你能告诉我这些的合乎逻辑的理由吗?
    • 是的,你是对的,这有点乏味,你可以从第一个实体扩展第二个实体。我在这里看到了一些 cmets coderanch.com/t/598699/ORM/databases/… 我不知道如何在 JPA 中做到这一点。
    • 我已经尝试过你的解决方案,它仍然给我同样的错误。我找到了这个解决方案,但无法得到它(关于配置的观点)stackoverflow.com/questions/1961566/…
    猜你喜欢
    • 1970-01-01
    • 2015-05-05
    • 2020-09-18
    • 2016-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多