简体   繁体   English

使用多个 Spring Boot 和 JDBC 数据源

[英]Using multiple Spring Boot and JDBC dataSources

I have the following issue that I have to solve in my business.我有以下问题需要在我的业务中解决。 I am using Spring for project development I have 8 dataSource to connect.我正在使用Spring进行项目开发,我有 8 个数据源要连接。

A request will be made informing the contract number, through this contact number I will have to select one of the 8 dataSource and make the client consultation.将请求告知合同编号,通过此联系电话,我必须选择 8 个数据源之一并进行客户咨询。

Example: I have the base Brazil = 1 , Spain = 2 and Germany = 3示例:我有基础Brazil = 1Spain = 2Germany = 3

  1. If the contract was id = 1 , then you should get the customer data from Brazil base .如果合同是id = 1 ,那么您应该从Brazil base获取客户数据。
  2. If the contract was id = 2 , then you should get the customer data from Spain base .如果合同是id = 2 ,那么您应该从Spain base获取客户数据。
  3. If the contract was id = 3 , then you should fetch customer data from the Germany base .如果合同是id = 3 ,那么您应该从Germany base获取客户数据。

I don't know how to solve this problem if I use multitenancy or AbstractRouting .如果我使用multitenancyAbstractRouting我不知道如何解决这个问题。 And I don't know how to start the code for this.我不知道如何为此启动代码。

Would anyone have any solution and an example?有没有人有任何解决方案和例子?

Spring has way to determine datasource dynamically using AbstractRoutingDataSource . Spring 可以使用AbstractRoutingDataSource动态确定数据源。 But need to use ThreadLocal to bind context to current thread.但需要使用ThreadLocal将上下文绑定到当前线程。

This may complicate things if you are spawning multiple threads or using async in your app.如果您在应用程序中生成多个线程或使用异步,这可能会使事情复杂化。

You can refer simple exmaple here你可以在这里参考简单的例子

If you really need to create database connection as per different clients then hibernate provides a way to manage Multi-tenancy , Hibernate Guide .如果您确实需要根据不同的客户端创建数据库连接,那么 hibernate 提供了一种管理Multi-tenancyHibernate Guide As explained general approach would be to use connection pool per-tenant or single connection pool per all tenants sharing same database but different schema.正如所解释的,一般方法是使用每个租户的连接池或每个共享相同数据库但不同架构的租户的单个连接池。

Below are the two implementation used to switch to multiple tenants -以下是用于切换到多个租户的两个实现 -

    public class TestDataSourceBasedMultiTenantConnectionProviderImpl
extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {

    private static final long serialVersionUID = 14535345L;

    @Autowired
    private DataSource defaultDataSource;

    @Autowired
    private TestDataSourceLookup dataSourceLookup;

    /**
     * 
     * Select datasources in situations where not tenantId is used (e.g. startup
     * processing).
     * 
     */
    @Override
    protected DataSource selectAnyDataSource() {
        //logger.trace("Select any dataSource: " + defaultDataSource);
        return defaultDataSource;
    }

    /**
     * 
     * Obtains a DataSource based on tenantId
     * 
     */

    @Override
    protected DataSource selectDataSource(String tenantIdentifier) {

        DataSource ds = dataSourceLookup.getDataSource(tenantIdentifier);
    //  logger.trace("Select dataSource from " + tenantIdentifier + ": " + ds);
        return ds;

    }

import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.springframework.beans.factory.annotation.Autowired;

public class TestCurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver  {

    @Autowired
    private RequestContext context;

    @Override
    public String resolveCurrentTenantIdentifier() {
        // TODO Auto-generated method stub
        return context.getTenantID();
    }

    @Override
    public boolean validateExistingCurrentSessions() {
        // TODO Auto-generated method stub
        return true;
    }

}

AbstractRouting would be a way to do it, indeed. AbstractRouting 确实是一种方法。 You can mix that approach with headers on the request to easily implement multi-tenancy.您可以将该方法与请求的标头混合使用,以轻松实现多租户。 The link provided in another post on Baeldung does indeed provide a solution using such construct.Baeldung上的另一篇文章中提供的链接确实提供了使用这种构造的解决方案。

Sharding and deploying a separate micro-service for each tenant, with a routing service in front, would be another one and I think it offers several benefits both from coding (no need for thread-locals or other similarly difficult construct) and maintenance perspective (you can take down your service for maintenance/customisation at tenant level), so it would be my solution of choice in your specific case, assuming that business requirements allow for it.为每个租户分片和部署一个单独的微服务,在前面有一个路由服务,这将是另一个,我认为它从编码(不需要线程局部或其他类似困难的构造)和维护角度提供了几个好处(您可以在租户级别取消您的服务以进行维护/定制),因此在您的特定情况下,这将是我选择的解决方案,假设业务需求允许。

Still, again, the best solution depends on your specific case.不过,最好的解决方案仍然取决于您的具体情况。

The easiest solution matching the title of the question - for readers that are not necessarily concerned with multi-tenancy - would be to create several dataSource instances in your Spring's @Configuration beans, put them in a Map<Integer, DataSource> and then return that Map<Integer, DataSource> object as a Bean.与问题标题相匹配的最简单的解决方案 - 对于不一定关心多租户的读者 - 是在 Spring 的@Configuration bean 中创建多个 dataSource 实例,将它们放入Map<Integer, DataSource>然后返回Map<Integer, DataSource>对象作为 Bean。 When you have to access them, you access the Map bean you created and then create your NamedParameterJdbcTemplate or similar resource passing the specific DataSource in the constructor.当您必须访问它们时,您可以访问您创建的Map bean,然后创建您的NamedParameterJdbcTemplate或类似资源,并在构造函数中传递特定的DataSource

Pseudo-code example:伪代码示例:

@Configuration
class DataSourceConfig {
    public final static int SPAIN = 2;
    public final static int BRAZIL = 1;

    @Bean
    @Qualifier("dataSources")
    public Map<Integer, DataSource> dataSources() {
        Map<Integer, DataSource> ds = new HashMap<>();
        ds.put(SPAIN, buildSpainDataSource());
        ds.put(BRAZIL, buildBrazilDataSource());
        return ds;
    }

    private DataSource buildSpainDataSource() {
        ...
    }

    private DataSource buildBrazilDataSource() {
        ...
    }

}

@Service
class MyService {
    @Autowired
    @Qualifier("dataSources")
    Map<Integer, DataSource> dataSources;

    Map<String, Object> getObjectForCountry(int country) {
        NamedParameterJdbcTemplate t = new NamedParameterJdbcTemplate(dataSources.get(country));
        return t.queryForMap("select value from mytable", new HashMap<>());
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM