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

时间:2018-12-18 12:06:35

标签: mysql spring-boot jpa jdbc multi-tenant

我正在创建一个多租户Spring Boot-JPA应用程序。

在此应用程序中,我想使用通过API请求作为标头发送的数据库名称连接到MySQL数据库。

我在线检查了许多多租户项目示例,但仍然找不到解决方案。

有人可以建议我这样做吗?

2 个答案:

答案 0 :(得分:1)

您可以使用AbstractRoutingDataSource来实现。 AbstractRoutingDataSource需要信息来知道要路由到哪个实际DataSource(称为 Context ),这是由determineCurrentLookupKey()方法提供的。使用here中的示例。

定义上下文,例如:

public enum ClientDatabase {
    CLIENT_A, CLIENT_B
}

然后,您需要定义将在determineCurrentLookupKey()

中使用的Context Holder。
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();
    }
}

然后您可以像下面这样扩展AbstractRoutingDataSource

public class ClientDataSourceRouter extends AbstractRoutingDataSource {

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

最后,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;
}

答案 1 :(得分:0)

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

按照这个逻辑,我现在可以解决它。一些版本需要升级,代码也需要升级。

  • Spring Boot版本已更改。

        org.springframework.boot     弹簧启动启动器父母     2.1.0。发布      

  • MySQL版本已被删除。

  • 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();
    }
    

    }

此更改是由于DataSourceBuilder已移至另一路径并且其构造函数已更改。

  • 还像这样在application.properties中更改了MySQL驱动程序类名称

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