将 OneToOne/ManyToOne 与 JPA 和多个数据源一起使用时出错

Posted

技术标签:

【中文标题】将 OneToOne/ManyToOne 与 JPA 和多个数据源一起使用时出错【英文标题】:Error using OneToOne/ManyToOne with JPA and Multiple Data Sources 【发布时间】:2014-02-03 16:02:56 【问题描述】:

我已经设法为 JPA 配置了多个数据源,但是在启动我的服务器(Websphere liberty)时出现以下错误:

org.hibernate.AnnotationException: @OneToOne or @ManyToOne onxxx.AccountBalance.currency references an unknown entity: xxx.Currency
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(T oOneFkSecondPass.java:109)
at org.hibernate.cfg.Configuration.processEndOfQueue( Configuration.java:1521)
at org.hibernate.cfg.Configuration.processFkSecondPas sInOrder(Configuration.java:1446)
at org.hibernate.cfg.Configuration.secondPassCompile( Configuration.java:1351)
at org.hibernate.cfg.Configuration.buildSessionFactor y(Configuration.java:1733)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>( EntityManagerFactoryImpl.java:94)
at org.hibernate.ejb.Ejb3Configuration.buildEntityMan agerFactory(Ejb3Configuration.java:905)

如果所有 DAO 都在同一个数据库中声明,则应用程序会正确部署,但如果我将其中任何一个移动到第二个数据库,则会失败。是否可以将 JPA 包(OneToOne、ManyToOne、ManyToMany)与多个数据源一起使用?

配置的相关部分:

上下文:

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/xxxwas"
cache="true" resource-ref="true" lookup-on-startup="false"
proxy-interface="javax.sql.DataSource" />

<bean id="h2dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url"
value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;TRACE_LEVEL_SYSTEM_OUT=3" />
<property name="username" value="test" />
<property name="password" value="test" />
</bean>

<tx:jta-transaction-manager />

<bean id="persistenceUnitManager"
class="org.springframework.orm.jpa.persistenceunit .DefaultPersistenceUnitManager">
<property name="persistenceXmlLocations">
<list>
<value>classpath:META-INF/persistence.xml</value>
</list>
</property>
<property name="dataSources">
<map>
<entry key="h2" value-ref="h2dataSource" />
<entry key="mysql" value-ref="dataSource" />
</map>
</property>
<!-- if no datasource is specified, use this one -->
<property name="defaultDataSource" ref="dataSource" />
</bean>

<bean id="integrationEntityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerE ntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="persistenceUnitManager" />
<property name="persistenceUnitName" value="integrationEntityManagerFactoryPU" />
<property name="jtaDataSource" ref="h2dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.Hibernat eJpaVendorAdapter">
<!-- <property name="showSql" value="true" /> -->
<property name="database" value="H2" />
<!-- <property name="generateDdl" value="true" /> -->
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>

<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerE ntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="persistenceUnitManager" />
<property name="persistenceUnitName" value="databaseEntityManagerFactoryPU" />
<property name="jtaDataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.Hibernat eJpaVendorAdapter">
<!-- <property name="showSql" value="true" /> -->
<property name="database" value="MYSQL" />
</bean>
</property>
</bean>

<jpa:repositories base-package="xxx.impl.repository.integration"
query-lookup-strategy="create-if-not-found"
entity-manager-factory-ref="integrationEntityManagerFactory">
</jpa:repositories>

<!-- Configures Spring Data JPA and sets the base package of my DAOs. -->
<jpa:repositories base-package="xxx.impl.repository"
query-lookup-strategy="create-if-not-found"
entity-manager-factory-ref="entityManagerFactory">
</jpa:repositories>

Server.xml

  <dataSource id="xxxwas" jndiName="jdbc/xxxwas" supplementalJDBCTrace="true" type="javax.sql.XADataSource">
        <jdbcDriver javax.sql.ConnectionPoolDataSource="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource" javax.sql.DataSource="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" javax.sql.XADataSource="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" libraryRef="MySQLLib"/>
        <properties databaseName="xxx" password="xxx" portNumber="3306" serverName="localhost" user="root"/>
    </dataSource>

Web.xml

<resource-ref>
    <res-ref-name>jdbc/xxxwas</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
    <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

Persistence.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="databaseEntityManagerFactoryPU" transaction-type="JTA">
        <class>xxx.impl.bo.AccountBalance</class>
       <!-- WORKS IF DEFINED HERE -->
        <!-- <class>xxx.impl.bo.Currency</class> -->
         <properties>
             <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.CMTTransactionFactory"/>
            <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.WebSphereExtendedJTATransactionLookup" />
            <!-- <property name="hibernate.current_session_context_class"value="thread" /> -->
            <!--<prop key="hibernate.transaction.flush_before_completion">false</prop>-->
            <!--<prop key="hibernate.transaction.auto_close_session">true</prop>-->
            <!--<prop key="hibernate.current_session_context_class">thread</prop>-->
            <!--<prop key="javax.persistence.transactionType">JTA</prop>-->
            <!--<prop key="hibernate.connection.release_mode">auto</prop>-->

            <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
            <property name="hibernate.cache.use_query_cache" value="true"/>
            <property name="hibernate.cache.use_second_level_cache" value="true"/>
            <property name="net.sf.ehcache.configurationResourceName" value="ehcache_database.xml"/>
            <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider"/>

            <!-- <property name="hibernate.archive.autodetection" value="class, hbm"/> -->
             <!-- <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>  -->
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.DefaultNamingStrategy"/>
            <property name="hibernate.use_sql_comments" value="true"/>
            <property name="hibernate.generate_statistics" value="true"/>
        </properties>
    </persistence-unit>
     <persistence-unit name="integrationEntityManagerFactoryPU" transaction-type="JTA">
           <!-- DOES NOT WORK IF DEFINED HERE -->
        <class>xxx.impl.bo.Currency</class>
        <properties>
             <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.CMTTransactionFactory"/>
            <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.WebSphereExtendedJTATransactionLookup" />
             <property name="hibernate.hbm2ddl.auto" value="create" />
        </properties>
    </persistence-unit>
</persistence>

【问题讨论】:

没有。您不能在数据库 x 中拥有实体 x,在数据库 y 中拥有实体 y,然后期望实体 x 和 y 一起工作。它们分开工作,但实体 x 和实体 y 之间不能有关系。 感谢您的回答 M. Deinum。你知道为什么这是不可能的吗? JPA 关系(一对一、一对多、多对多)是通过实体管理器还是通过其他机制解决的?了解为什么此功能不可用会很有趣,如果它永远可用,我很确定它在许多多租户场景中会很有用。 相关实体通过相同的EntityManager 解析,因此不能存在于不同的数据库中。必须在同一个EntityManager 中访问所有内容。如果您想将其拆分到不同的数据库中,则必须手动进行管理。 我一直在寻找不同的解决方案,使用 AbtractRoutingDataSource 创建动态路由数据源并在单个 entityManagerFactory link 中使用它。不幸的是,这种方法(如果有效)将仅限于共享相同方言的多个数据库,对吗?除了删除关系(一对一、一对多、多对多)之外,还有其他您可以考虑的替代方法吗? 如前所述手动操作。您不能,也不应该尝试在 JPA 规范下硬塞一些不属于规范的东西。此外,如果它们位于 2 个不同的数据库中,您可能需要重新考虑这些关系。此外,您的域不必与数据库对象相同! (我建议阅读 Eric Evans 的 Domain Driven Design。 【参考方案1】:

我知道为时已晚,但我遇到了同样的问题。 我正在研究 Oracle 数据库,同一个数据库有两个模式(用户) 我的解决方案是让第一个用户访问第二个用户模式中的所有表。 之后,在 JPA Entity annotation 上,为每个实体精确的 schema。

这样,hibernate 会生成带有 schema 的 SQL 查询:

select field1, field2 from USER1.Table1 INNER JOIN USER2.TABLE2 ON .....

之所以这样工作,是因为 user1 可以通过授权访问 user2 表,但是这两个模式必须在同一个数据库中,否则您必须创建一个 dblink 和一个同义词。

【讨论】:

以上是关于将 OneToOne/ManyToOne 与 JPA 和多个数据源一起使用时出错的主要内容,如果未能解决你的问题,请参考以下文章

Firefox 可以显示 JP2 图像吗?

JP柜组装流水线制造工艺及用途

如何将 jp2 图像转换为 jpg 文件?

译使用WebDriver采样器将JMeter与Selenium集成

将 android app bundle 上传到 playstore 时出错。。您的 app bundle 的目标是无法识别的语言 jp

JP是啥??和js有啥区别?能举例说明嘛?感激不尽