需要动态更改数据源URL的数据库名称(多租户数据库)

时间:2019-04-16 11:26:11

标签: java spring-boot spring-data-jpa datasource

我正在使用Spring Boot和Spring-data-Jpa。我在application.properties文件中设置数据源URL,用户名和密码。它非常适合一个数据库连接,现在我的数据库项目结构面临一个问题,该项目基于特定用户,他自己的数据库需要连接并将结果连接到特定用户数据库,我可以使用抽象数据源来实现这一点,在配置级别使用DataSourceBuilder(这是我一次可以动态更改数据源),但是每次控制器命中时我都需要更改数据源。

这是一些application.properties的代码,我已经使用autowire注入了数据源。

我使用的抽象数据源仅限于静态客户端,但是在我的结构中,“客户端数据库”一直在增长,因此对我没有用

spring.datasource.url=jdbc:sqlserver://test-datbase:1433;dbName1 spring.datasource.username=userName spring.datasource.password=Password spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

需要代码或方法,每次点击控制器时我都可以更改数据库连接

注意:我只需要更改数据库,方言和其他所有内容即可。

5 个答案:

答案 0 :(得分:1)

是的,我们可以使用占位符来做到这一点。在环境变量中设置-DdbName1=YOUR_DB_NAME。例如:

spring.datasource.url=jdbc:sqlserver://test-datbase:1433;${dbName1}
spring.datasource.username=userName
spring.datasource.password=Password
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

答案 1 :(得分:0)

这是我解决此类问题的方法: 您可以创建2个单独的数据源。为它们创建限定符,并将两者都注入到您的控制器中。 然后在端点中编写逻辑,该逻辑将选择源之一来保存信息。

以下是向项目添加额外数据源的方法:

https://medium.com/@joeclever/using-multiple-datasources-with-spring-boot-and-spring-data-6430b00c02e7

答案 2 :(得分:0)

我认为在这种情况下使用Wildfly是一个好主意。在Wildfly,您可以使用设置更改连接的数据库。

我的解决方案: enter link description here

,并且在选择数据库时请编写自己的PersistenceConfiguration类 enter link description here

如果您想使用Java代码中的方法动态选择基础

答案 3 :(得分:0)

借助下面的链接,我可以在服务器启动时设置多个数据源

https://fizzylogic.nl/2016/01/24/make-your-spring-boot-application-multi-tenant-aware-in-2-steps/

但是我想删除如下配置注释,并使用如下方法设置租户,但是通过此方法我无法连接到数据库。

public class MultitenantConfiguration {
  @Bean
@ConfigurationProperties(
        prefix = "spring.datasource"
)
public DataSource dataSource(ArrayList<String> names) {


    Map<Object,Object> resolvedDataSources = new HashMap<>();

    for(String  dbName: names) {

        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader());
        dataSourceBuilder.driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
                .url("jdbc:sqlserver://abc.server;databaseName="+dbName+"")
                .username("userName")
                .password("Password");


        resolvedDataSources.put(dbName, dataSourceBuilder.build());
    }



    MultitenantDataSource dataSource = new MultitenantDataSource();
    dataSource.setDefaultTargetDataSource(defaultDataSource());
    dataSource.setTargetDataSources(resolvedDataSources);
    dataSource.afterPropertiesSet();


    return dataSource;
}

/**
 * Creates the default data source for the application
 * @return
 */
private DataSource defaultDataSource() {
    DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader())
            .driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
            .url("jdbc:abc.server;databaseName=test")
            .username("UserName")
            .password("Password");

    return dataSourceBuilder.build();
}
  }

答案 4 :(得分:0)

我已经完成了一个项目,可以使用您的特定changeSets创建多个dataSources,因此,如果您需要添加另一个dataSource,则只需更改application.yml,而无需更改代码。 但是,如果不使用,也可以删除也可以使用的liquibase!

在控制器的每次命中中,您需要获取X-TenantId标头,该标头将更改您的ThreadLocal,从而根据租户更改数据源

代码完整:https://github.com/dijalmasilva/spring-boot-multitenancy-datasource-liquibase

application.yml

spring:
  dataSources:
    - tenantId: db1
      url: jdbc:postgresql://localhost:5432/db1
      username: postgres
      password: 123456
      driver-class-name: org.postgresql.Driver
      liquibase:
        enabled: true
        default-schema: public
        change-log: classpath:db/master/changelog/db.changelog-master.yaml
    - tenantId: db2
      url: jdbc:postgresql://localhost:5432/db2
      username: postgres
      password: 123456
      driver-class-name: org.postgresql.Driver
    - tenantId: db3
      url: jdbc:postgresql://localhost:5432/db3
      username: postgres
      password: 123456
      driver-class-name: org.postgresql.Driver

TenantContext

public class TenantContext {

    private static ThreadLocal<String> currentTenant = new ThreadLocal<>();

    static String getCurrentTenant() {
        return currentTenant.get();
    }

    static void setCurrentTenant(String tenant) {
        currentTenant.set(tenant);
    }

    static void clear() {
        currentTenant.remove();
    }
}

过滤到控制器

public class TenantFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        final String X_TENANT_ID = "X-TenantID";

        final HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        final String tenantId = httpServletRequest.getHeader(X_TENANT_ID);

        if (tenantId == null) {
            final HttpServletResponse response = (HttpServletResponse) servletResponse;
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getWriter().write("{\"error\": \"No tenant header supplied\"}");
            response.getWriter().flush();
            TenantContext.clear();
            return;
        }

        TenantContext.setCurrentTenant(tenantId);
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

配置类(如果使用liquibase)

@Configuration
@ConditionalOnProperty(prefix = "spring.liquibase", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(LiquibaseProperties.class)
@AllArgsConstructor
public class LiquibaseConfiguration {

    private LiquibaseProperties properties;
    private DataSourceProperties dataSourceProperties;

    @Bean
    @DependsOn("tenantRoutingDataSource")
    public MultiTenantDataSourceSpringLiquibase liquibaseMultiTenancy(Map<Object, Object> dataSources,
                                                                      @Qualifier("taskExecutor") TaskExecutor taskExecutor) {
        // to run changeSets of the liquibase asynchronous
        MultiTenantDataSourceSpringLiquibase liquibase = new MultiTenantDataSourceSpringLiquibase(taskExecutor);
        dataSources.forEach((tenant, dataSource) -> liquibase.addDataSource((String) tenant, (DataSource) dataSource));
        dataSourceProperties.getDataSources().forEach(dbProperty -> {
            if (dbProperty.getLiquibase() != null) {
                liquibase.addLiquibaseProperties(dbProperty.getTenantId(), dbProperty.getLiquibase());
            }
        });

        liquibase.setContexts(properties.getContexts());
        liquibase.setChangeLog(properties.getChangeLog());
        liquibase.setDefaultSchema(properties.getDefaultSchema());
        liquibase.setDropFirst(properties.isDropFirst());
        liquibase.setShouldRun(properties.isEnabled());
        return liquibase;
    }

}

代码完整:https://github.com/dijalmasilva/spring-boot-multitenancy-datasource-liquibase