使用多个数据源时 Spring 中的多个实体管理器问题
Posted
技术标签:
【中文标题】使用多个数据源时 Spring 中的多个实体管理器问题【英文标题】:Multiple Entity Manager issue in Spring when using more than one datasource 【发布时间】:2011-04-13 11:03:09 【问题描述】:我的applicationContext.xml
中有两个实体管理器,它们对应于两个不同的数据库。我可以使用entityManager1
轻松查询database1
,但是当我尝试使用entityManager2
访问database2
时,我没有得到任何结果。我正在使用 Spring+Hibernate+JPA。
这是我的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>
这是我的服务层代码,可以很好地与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;
我的两个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>
和
<?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>
如果我更改我的服务层(如下所示),我不会得到任何结果。基本上列表的大小为零:
@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;
所以基本上你可以看到我有两个结构完全相同的实体(状态),为了彼此区分,我将它们放入单独的包中
据我所知,我在这里并没有做任何疯狂的事情,但它似乎仍然不起作用。这个问题是怎么引起的,我该如何解决?
后续:我忘了提一件事,即使有两个不同的数据库,但数据库服务器名称是相同的。我不知道这是否是一个有用的信息。所以想分享它。
这是我现在遇到的异常:
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
【问题讨论】:
您仍在为上述示例中的 PU1 注入 EM,是不是拼写错误?为什么你实际上有两个版本的State
?有什么意义?
嗨,帕斯卡,是的!这是一个错字。我也尝试过使用单一状态版本,但仍然是同样的问题。之所以有两个状态,是因为在一个数据库中,状态表具有一对多关系,而在第二个数据库中则没有。即使结构相同但约束不同。
请修正错字。这是一条红鲱鱼。
我已修正错字。现在请提出一些建议。
不过,我不知道如何解释您收到的错误。您是否尝试提高日志记录级别(到 DEBUG)以查看是否获得了更多有用的跟踪信息?
【参考方案1】:
我遇到了同样的问题,但是有多个 Hibernate 会话工厂:2 个具有相同结构的 DB,我不希望有 2 个相同的 DAO 集,等等。虽然我的经验是使用 Hibernate,但我怀疑您可以使用相同的解决方案:Spring 的AbstractRoutingDataSource
。它允许您配置您的应用程序以在运行时根据在ThreadLocal
上设置的值确定要使用的数据源。有关介绍,请参阅http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/。最终发生的事情是,您工厂中的dataSource
ref 不会指向硬编码的dataSource
bean,而是指向AbstractRoutingDataSource
。要设置每个线程的切换,请使用 @Aspect 来确定要命中哪个数据库。
【讨论】:
以上是关于使用多个数据源时 Spring 中的多个实体管理器问题的主要内容,如果未能解决你的问题,请参考以下文章