在DAO中使用多个实体管理器

时间:2014-01-09 19:38:13

标签: java hibernate spring-mvc jpa

我正在尝试将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-调度-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>

我的道:

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 
}

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

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

添加两个类

<强> ContactWrite.java 在顶部声明架构和表格如下

  

@Table(name =&#34; Contact&#34;,schema =&#34; DB1&#34;)

对DB2中的其他表执行相同的操作

<强> ContactRead.java

  

@Table(name =&#34; Contact&#34;,schema =&#34; DB2&#34;)

现在在persistance 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>

答案 2 :(得分:0)

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

@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最终也必须从数据库中读取,相信我)。