java 使用多个数据源时,Spring 中的多个实体管理器问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3731016/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 03:12:26  来源:igfitidea点击:

Multiple Entity Manager issue in Spring when using more than one datasource

javaspringjpadao

提问by Sameer Malhotra

I have two entity managers in my applicationContext.xmlwhich corresponds to two different databases. I can easily query database1with entityManager1, but when I try to access database2with entityManager2, I am not getting any results. I am using Spring+Hibernate+JPA.

我有两个实体管理器,applicationContext.xml它们对应于两个不同的数据库。我可以很容易地查询database1entityManager1,但是当我试图访问database2entityManager2,我没有得到任何结果。我正在使用 Spring+Hibernate+JPA。

Here is my ApplicationContext.xml

这是我的 ApplicationContext.xml

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

    <beans default-autowire="byName"
     xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

 <bean
  class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />


 <bean id="entityManagerFactory"
  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="jpaVendorAdapter">
   <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="database" value="INFORMIX" />
    <property name="showSql" value="true" />
   </bean>
  </property>
  <property name="persistenceUnitManager" ref="persistenceUnitManager" />
  <property name="persistenceUnitName" value="PU1" />
 </bean>



 <bean id="entityManagerFactory2"
  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="dataSource2" />
  <property name="jpaVendorAdapter">
   <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="database" value="INFORMIX" />
    <property name="showSql" value="true" />
   </bean>
  </property>
  <property name="persistenceUnitManager" ref="persistenceUnitManager" />
  <property name="persistenceUnitName" value="PU2" />
 </bean>



 <!-- Data Sources -->

 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
  destroy-method="close">
  <property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver" />
  <property name="url"
   value="jdbc:db2://HOST_NAME:PORT_NO/DB_NAME:INFORMIXSERVER=SERVER_NAME;DELIMIDENT=y;" />
  <property name="username" value="username" />
  <property name="password" value="password" />
  <property name="minIdle" value="2" />
 </bean>

 <bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource"
  destroy-method="close">
  <property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver" />
  <property name="url"
   value="jdbc:db2://HOST_NAME:PORT_NO/DB_NAME2:INFORMIXSERVER=SERVER_NAME;DELIMIDENT=y;" />
  <property name="username" value="username" />
  <property name="password" value="password" />
  <property name="minIdle" value="2" />
 </bean>

 <bean
  class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
  lazy-init="false">
  <property name="targetObject" ref="dataSource" />
  <property name="targetMethod" value="addConnectionProperty" />
  <property name="arguments">
   <list>
    <value>characterEncoding</value>
    <value>UTF-8</value>
   </list>
  </property>
 </bean>

 <bean
  class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
  lazy-init="false">
  <property name="targetObject" ref="dataSource2" />
  <property name="targetMethod" value="addConnectionProperty" />
  <property name="arguments">
   <list>
    <value>characterEncoding</value>
    <value>UTF-8</value>
   </list>
  </property>
 </bean>

 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
  scope="prototype">
  <property name="dataSource" ref="dataSource" />
 </bean>



 <bean id="persistenceUnitManager"
  class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
  <property name="persistenceXmlLocations">
   <list>
    <value>classpath*:META-INF/persistence.xml</value>
    <value>classpath*:META-INF/persistence2.xml</value>
   </list>
  </property>
  <property name="dataSources">
   <map>
    <entry key="localDataSource" value-ref="dataSource" />
    <entry key="dataSource2" value-ref="dataSource2" />
   </map>
  </property>
  <property name="defaultDataSource" ref="dataSource" />
 </bean>



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

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

 <tx:annotation-driven transaction-manager="transactionManager" />
 <tx:annotation-driven transaction-manager="transactionManager2" />

 <!-- MORE Action and DAO beans -->


</beans>

This is my service layer code which works fine with enityManager1:

这是我的服务层代码,适用于enityManager1

  @Transactional
    public class StatesDAO implements IStatesDAO {
 private EntityManager em;

 @PersistenceContext(unitName = "PU1")
 public void setEntityManager(EntityManager em) {
  this.em = em;
 }

 private EntityManager getEntityManager() {
  return em;
 }

 @SuppressWarnings("unchecked")
 public List<States> findAll() {
  logger.info("finding all States instances");
  try {
   final String queryString = "select model from States model";

   Query query = getEntityManager().createQuery(queryString);
   return query.getResultList();

  } catch (RuntimeException re) {
   throw re;
  }

 }

    }

My two persitence.xmlfiles look like this:

我的两个persitence.xml文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
 <persistence-unit name="PU1" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <class>com.jpa.entity.States</class>
 </persistence-unit>
</persistence>

and

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
 <persistence-unit name="PU2" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <class>com.jpa.other.entity.States</class>
 </persistence-unit>
</persistence>

If I change my service layer (as shown below), I get no results. Basically the size of the list is zero:

如果我更改我的服务层(如下所示),我将不会得到任何结果。基本上列表的大小为零:

@Transactional
public class StatesDAO implements IStatesDAO {
private EntityManager em;

@PersistenceContext(unitName = "PU2")
public void setEntityManager(EntityManager em) {
    this.em = em;
}

private EntityManager getEntityManager() {
    return em;
}

@SuppressWarnings("unchecked")
public List<com.jpa.other.entity.States> findAll() {
    logger.info("finding all States instances");
    try {
        final String queryString = "select model from States model";

        Query query = getEntityManager().createQuery(queryString);
        return query.getResultList();

    } catch (RuntimeException re) {
        throw re;
    }

}

}

So basically you can see is that I have two entities(States) with exactly same structure and in order to differentiate from each other I have put them into separate packages

所以基本上你可以看到我有两个具有完全相同结构的实体(状态),为了彼此区分,我将它们放入单独的包中

According to my knowledge I am not doing anything crazy here but still it doesn't seem to be working. How is this problem caused and how can I solve this?

据我所知,我在这里没有做任何疯狂的事情,但它似乎仍然不起作用。这个问题是如何引起的,我该如何解决?

Follow-up:One thing I forgot to mention is that even though there are two different databases but the database server name is same. I don't know if this could be a useful information.So thought of sharing it.

跟进:我忘记提及的一件事是,即使有两个不同的数据库,但数据库服务器名称是相同的。我不知道这是否是一个有用的信息。所以想分享它。

This is the exception I am getting now:

这是我现在得到的例外:

16:24:44,732 INFO [STDOUT] Hibernate: select state0_.state as col_0_0_ from states state0_ 
16:24:44,753 WARN [JDBCExceptionReporter] SQL Warning: 36106, SQLState: 01I01 
16:24:44,753 WARN [JDBCExceptionReporter] IDS SQL Warning: SQLCODE=36106, SQLSTATE=01I01, SQLERRMC=0;819;informix;;IDS/NT32;1;1;0;819;0;, DRIVER=4.7.85 

回答by atrain

I've hit the same exact issue, but with multiple Hibernate session factories: 2 DBs with the same structure, I didn't want to have 2 identical sets of DAOs, etc. While my experience was with Hibernate, I suspect you could use the same solution: Spring's AbstractRoutingDataSource. It allows you to configure your app to determine at runtime which data source to use, based on a value set on the ThreadLocal. See http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/for an introduction. What ends up happening is that the dataSourceref in your factory will point not at a hard-coded dataSourcebean, but at the AbstractRoutingDataSource. To set the toggle per-thread, use an @Aspect to determine which DB to hit.

我遇到了同样的问题,但是有多个 Hibernate 会话工厂:2 个结构相同的 DB,我不想有 2 组相同的 DAO,等等。虽然我的经验是 Hibernate,但我怀疑你可以使用相同的解决方案:Spring 的AbstractRoutingDataSource. 它允许您配置您的应用程序,以在运行时根据ThreadLocal. 有关介绍,请参阅http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/。最终发生的事情是dataSource你工厂中的ref 将不会指向一个硬编码的dataSourcebean,而是指向AbstractRoutingDataSource. 要设置每个线程的切换,请使用 @Aspect 来确定要命中哪个 DB。