具有多个数据源但只有一组存储库的 Spring Data + JPA [英] Spring Data + JPA with multiple datasources but only one set of Repositories

查看:29
本文介绍了具有多个数据源但只有一组存储库的 Spring Data + JPA的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我今天一直在研究这个,我开始认为我想做的事情可能是不可能的,所以我向你求助,哦强大的 Stackoverflow,寻求帮助.

I've been researching this a bunch today and I'm starting to think that what I want to do may not be possible, so I am turning to you, o mighty Stackoverflow, for help.

我正在用 Java 构建一个 RESTful 服务平台,使用 Spring Data 3.1.2 + JPA 作为我的持久层(如文档所述 此处).我的数据模型对象都实现为扩展 Spring JpaRepository 接口的接口.我已经把所有东西都连接好,并且可以很好地使用单个数据源,如本示例所示(请注意,显示的数据源是 Derby,但这仅用于开发目的;在生产中,我们将使用 Oracle):

I'm building a RESTful services platform in Java, with Spring Data 3.1.2 + JPA as my persistence layer (as documented here). My data model objects are all implemented as interfaces that extend the Spring JpaRepository interface. I've got everything wired up and working nicely with a single datasource, as shown in this example (note that the datasource shown is Derby, but that's just for development purposes; in production, we'll be using Oracle):

<jpa:repositories base-package="com.my.cool.package.repository"/>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="loadTimeWeaver">
    <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
  </property>
  <property name="packagesToScan" value="com.my.cool.package" />
  <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
</bean>

<bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="emf" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
  <property name="url" value="jdbc:derby:derbyDB" />
  <property name="username" value="dev" />
  <property name="password" value="notARealPassword" />
</bean>

<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
  <property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" />
</bean>

问题是此应用程序需要连接到多个 (Oracle) 数据库.每个传入请求中包含的凭据将包含一个字段,该字段告诉应用程序要转到哪个数据库来满足该请求.每个数据库的架构都相同,因此不需要为每个数据库设置单独的存储库接口.

The problem is that this application will need to connect to several (Oracle) databases. The credentials included with each incoming request will contain a field that tells the application which database to go to in order to fulfill that request. The schemas for each database are the same, so there's no need for separate repository interfaces for each database.

经过大量的谷歌搜索后,很明显这是一个常见的场景.即:

After a fair amount of Googling, it's clear that this is a common scenario. To wit:

这是一个(前?)Spring 开发人员的博客文章,它实际上与手头的主题无关,但有人在评论中提出了它,作者回复了一些信息:

And here's a blog post by a (former?) Spring developer, which isn't actually relevant to the topic at hand, but someone brings it up in the comments, and the author responds with some info:

http://blog.springsource.org/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/#comment-198835

似乎正在出现的主题是解决这个问题的方法是定义多个 EntityManagerFactories,并将每个连接到适当的存储库,如下所示:

The theme that seems to be emerging is that the way to solve this problem is to define multiple EntityManagerFactories, and to wire each one to the appropriate repositories like so:

<jpa:repositories base-package="com.my.cool.package.repository1" entity-manager-factory-ref="firstEntityManagerFactory" />
<jpa:repositories base-package="com.my.cool.package.repository2" entity-manager-factory-ref="secondEntityManagerFactory" />

然而,正如我所提到的,我想在所有数据源中重用我的存储库,所以这种方法似乎行不通.

However, as I've mentioned, I want to reuse my repository across all of the datasources, so this approach doesn't seem like it would work.

我知道在我的代码中没有办法避免从请求中获取相关信息的逻辑,并使用它来确定要使用的数据源(或 EntityManagerFactory).我正在努力解决的部分是如何获取该数据源/EntityManagerFactory 的句柄并将其注入"到我的存储库对象中.有什么想法吗?

I know that there's no way around having logic in my code that takes the relevant piece of information from the request, and uses it to determine which datasource (or EntityManagerFactory) to use. The part I'm struggling with is how to get a handle to that datasource/EntityManagerFactory and "inject" it into my repository objects. Any ideas?

推荐答案

如果您确实以多租户的方式使用不同的 DataSourcees(本质上是将请求分配给 DataSourcecode>DataSource 并在整个请求中坚持使用它)你应该看看 AbstractRoutingDataSource.它本质上提供了一种方法来保留 DataSourceMap 以及返回用于查找 DataSource 的键的回调方法最终被使用.这个方法的实现通常会查找一些线程绑定的键并返回它(或者甚至将它依次映射到 DataSource 映射键).您只需要首先确保某个 Web 组件将该键绑定到线程即可.

If you're really using the different DataSourcees in a multi-tenant kind of way (essentially assigning a request to a DataSource and sticking with it for the entire request) you should have a look at AbstractRoutingDataSource. It essentially provides a way to keep a Map of DataSourcees as well as a callback method to return a key to be used to lookup the DataSource to be used eventually. The implementation of this method usually looks up some thread bound key and return that (or even maps that onto a DataSource map key in turn). You just have to make sure some web component binds that key to the thread in the first place.

如果你有这样的配置,你的 Spring 配置只需为 AbstractRoutingDataSource 的子类设置一个 bean,并将 DataSources 的映射传送到它.您的 Spring Data JPA 设置保持默认方式.EntityManagerFactoryBean 指的是 AbstractRoutingDataSource 并且您只有一个 元素.

If you have that in place your Spring configuration just sets up a bean for your sub-class of AbstractRoutingDataSource and pipes the map of DataSources into it. Your Spring Data JPA setup stays the default way. The EntityManagerFactoryBean refers to the AbstractRoutingDataSource and you have a single <jpa:repositories /> element only.

这篇关于具有多个数据源但只有一组存储库的 Spring Data + JPA的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆