简体   繁体   English

如何根据REST API请求中的标头连接到多个MySQL数据库

[英]How to connect to multiple MySQL databases as per the header in REST API request

I'm creating a multi tenant spring boot - JPA application. 我正在创建一个多租户Spring Boot-JPA应用程序。

In this application, I want to connect to MySQL Databases using DB name which is sent through API request as header. 在此应用程序中,我想使用通过API请求作为标头发送的数据库名称连接到MySQL数据库。

I checked many multi tenant project samples online but still can't figure out a solution. 我在线检查了许多多租户项目示例,但仍然找不到解决方案。

Can anyone suggest me a way to do this? 有人可以建议我这样做吗?

You can use AbstractRoutingDataSource to achieve this. 您可以使用AbstractRoutingDataSource来实现。 AbstractRoutingDataSource requires information to know which actual DataSource to route to(referred to as Context ), which is provided by determineCurrentLookupKey() method. AbstractRoutingDataSource需要一些信息来知道要路由到哪个实际DataSource (称为Context ),该信息由determineCurrentLookupKey()方法提供。 Using example from here . 这里使用示例。

Define Context like: 定义上下文,例如:

public enum ClientDatabase {
    CLIENT_A, CLIENT_B
}

Then you need to define Context Holder which will be used in determineCurrentLookupKey() 然后,您需要定义Context Holder,该上下文将在determineCurrentLookupKey()

public class ClientDatabaseContextHolder {

    private static ThreadLocal<ClientDatabase> CONTEXT = new ThreadLocal<>();

    public static void set(ClientDatabase clientDatabase) {
        Assert.notNull(clientDatabase, "clientDatabase cannot be null");
        CONTEXT.set(clientDatabase);
    }

    public static ClientDatabase getClientDatabase() {
        return CONTEXT.get();
    }

    public static void clear() {
        CONTEXT.remove();
    }
}

Then you can extend AbstractRoutingDataSource like below: 然后,您可以扩展AbstractRoutingDataSource如下所示:

public class ClientDataSourceRouter extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return ClientDatabaseContextHolder.getClientDatabase();
    }
}

Finally, DataSource bean configuration: 最后, DataSource bean配置:

@Bean
public DataSource clientDatasource() {
    Map<Object, Object> targetDataSources = new HashMap<>();
    DataSource clientADatasource = clientADatasource();
    DataSource clientBDatasource = clientBDatasource();
    targetDataSources.put(ClientDatabase.CLIENT_A, 
      clientADatasource);
    targetDataSources.put(ClientDatabase.CLIENT_B, 
      clientBDatasource);

    ClientDataSourceRouter clientRoutingDatasource 
      = new ClientDataSourceRouter();
    clientRoutingDatasource.setTargetDataSources(targetDataSources);
    clientRoutingDatasource.setDefaultTargetDataSource(clientADatasource);
    return clientRoutingDatasource;
}

https://github.com/wmeints/spring-multi-tenant-demo https://github.com/wmeints/spring-multi-tenant-demo

Following this logic, I can solve it now. 按照这个逻辑,我现在可以解决它。 Some of the versions need to be upgraded and the codes as well. 一些版本需要升级,代码也需要升级。

  • Spring Boot version have changed. Spring Boot版本已更改。

    org.springframework.boot spring-boot-starter-parent 2.1.0.RELEASE org.springframework.boot spring-boot-starter-parent 2.1.0.RELEASE

  • Mysql version has been removed. Mysql版本已被删除。

  • And some small changed in MultitenantConfiguration.java MultitenantConfiguration.java一些小的更改

     @Configuration public class MultitenantConfiguration { @Autowired private DataSourceProperties properties; /** * Defines the data source for the application * @return */ @Bean @ConfigurationProperties( prefix = "spring.datasource" ) public DataSource dataSource() { File[] files = Paths.get("tenants").toFile().listFiles(); Map<Object,Object> resolvedDataSources = new HashMap<>(); if(files != null) { for (File propertyFile : files) { Properties tenantProperties = new Properties(); DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader()); try { tenantProperties.load(new FileInputStream(propertyFile)); String tenantId = tenantProperties.getProperty("name"); dataSourceBuilder.driverClassName(properties.getDriverClassName()) .url(tenantProperties.getProperty("datasource.url")) .username(tenantProperties.getProperty("datasource.username")) .password(tenantProperties.getProperty("datasource.password")); if (properties.getType() != null) { dataSourceBuilder.type(properties.getType()); } resolvedDataSources.put(tenantId, dataSourceBuilder.build()); } catch (IOException e) { e.printStackTrace(); return null; } } } // Create the final multi-tenant source. // It needs a default database to connect to. // Make sure that the default database is actually an empty tenant database. // Don't use that for a regular tenant if you want things to be safe! MultitenantDataSource dataSource = new MultitenantDataSource(); dataSource.setDefaultTargetDataSource(defaultDataSource()); dataSource.setTargetDataSources(resolvedDataSources); // Call this to finalize the initialization of the data source. dataSource.afterPropertiesSet(); return dataSource; } /** * Creates the default data source for the application * @return */ private DataSource defaultDataSource() { DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader()) .driverClassName(properties.getDriverClassName()) .url(properties.getUrl()) .username(properties.getUsername()) .password(properties.getPassword()); if(properties.getType() != null) { dataSourceBuilder.type(properties.getType()); } return dataSourceBuilder.build(); } 

    } }

This change is here due to the DataSourceBuilder has been moved to another path and its constructor has been changed. 发生此更改是由于DataSourceBuilder已移至另一个路径,并且其构造函数已更改。

  • Also changed the MySQL driver class name in application.properties like this 还像这样在application.properties更改了MySQL驱动程序类名称

    spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.driver类名= com.mysql.jdbc.Driver

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

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