首页 > 解决方案 > Liquibase 支持多租户环境中的多个架构

问题描述

我正在开发一个多租户环境,该环境具有共享数据库和每个租户的独立架构。

我的租户表如下:

id|tenant_id|tenant_schema_name|company_flg|
--|---------|------------------|-----------|

还有一个 app_roles 表,计划在其中使用 liquibase 脚本本身播种一些数据。

app_roles 表如下:

role_id|role_nm   |
-------|----------|
      1|ROLE_ADMIN|
      2|ROLE_USER |

为 defaultSchema(全局模式)中存在的所有租户运行 liquibase 脚本的 Bean 是:

@Bean
public MultiTenantSpringLiquibase liquibaseMt(DataSource dataSource)
        throws SQLException {
    MultiTenantSpringLiquibase multiTenantSpringLiquibase = new MultiTenantSpringLiquibase();
    multiTenantSpringLiquibase.setDataSource(dataSource);

    Statement stmt = null;
    stmt = dataSource.getConnection().createStatement();

    String sql = "SELECT tenant_schema_name FROM {defaultSchema}.tenant" ;
    sql = sql.replace("{defaultSchema}", AllConstants.defaultSchema);
    ResultSet rs = stmt
            .executeQuery(sql);
    List<String> schemas = new ArrayList<>();
    while (rs.next()) {
        String schemaName = rs.getString("tenant_schema_name");
        logger.info(schemaName);
        dataSource.getConnection().createStatement()
                .executeUpdate("CREATE SCHEMA IF NOT EXISTS " + schemaName);
        schemas.add(schemaName);
    }
    multiTenantSpringLiquibase.setSchemas(schemas);
    multiTenantSpringLiquibase.setChangeLog(AllConstants.liquibasePath);
    multiTenantSpringLiquibase.setShouldRun(true);      
    if(schemas.isEmpty()) {
        multiTenantSpringLiquibase.setDefaultSchema(AllConstants.defaultSchema);
    }
    return multiTenantSpringLiquibase;
}

上面提到的 bean 为每个租户创建模式,并为租户的每个模式执行 liquibase。

下面是我的 liquibase 脚本:

db.changelog-master.yaml:

databaseChangeLog:
- include:
    file: db/changelog/changes/db.changelog-create-v1.yml
- include:
    file: db/changelog/changes/db.changelog-seed-v1.yml

db.changelog-create-v1.yml:

databaseChangeLog:
              
- changeSet: 
    id: author-create-3
    author: author
    preCondition:
      onFail: MARK_RAN
      not:
        tableExists: 
          tableName: APP_ROLES
    changes:
    - createTable: 
        tableName: APP_ROLES
        columns:
          - column:
              name: ROLE_ID
              constraints:
                primaryKey: true
                nullable: false
                primaryKeyName: APP_ROLE_PK
              type: INTEGER
          - column:
              name: ROLE_NM
              constraints:
                nullable: false
              type: VARCHAR(50)
   
- changeSet:
    id: author-create-7
    author: author
    preConditions:
      onFail: MARK_RAN
      not:
        sequenceExists:
          sequenceName: TENANT_SEQ
    changes:
    - createSequence:
         sequenceName: TENANT_SEQ
         startValue: 1
         minValue: 1
         incrementBy: 1

- changeSet: 
    id: author-create-8
    author: author
    preCondition:
      onFail: MARK_RAN
      not:
        tableExists: 
          tableName: TENANT
    changes:
    - createTable: 
        tableName: TENANT
        columns:
          - column:
              name: ID
              constraints:
                primaryKey: true
                nullable: false
                primaryKeyName: TENANT_PK
              type: INTEGER
          - column:
              name: TENANT_ID
              type: VARCHAR(50)
          - column:
              name: TENANT_SCHEMA_NAME
              type: VARCHAR(50)
          - column:
              name: COMPANY_FLG
              type: BOOLEAN

db.changelog-seed-v1.yml:

databaseChangeLog:
    
- changeSet:
    id: author-seed-1
    author: author
    preConditions:
        onFail: MARK_RAN
        sqlCheck:
            expectedResult: 0
            sql: select count(*) from app_roles;
    changes:
    - sql:
        sql: INSERT INTO app_roles (role_id, role_nm) VALUES (1, 'ROLE_ADMIN');
             INSERT INTO app_roles (role_id, role_nm) VALUES (2, 'ROLE_USER');

这里有问题的部分是创建脚本成功地为每个租户运行并为我创建了所需的表,但是种子脚本仅为默认模式和所有其他先决条件播种数据,它从默认模式中找到 2 行并绕过每个租户。

在这里,当应用程序第一次启动时,会创建默认模式(全局模式)。我在全局模式中使用 sql 查询创建租户并重新启动应用程序,以便为所有插入的 sql 租户运行 liquibase 脚本。

我如何以编程方式逻辑解决方案是以编程方式要求种子脚本针对特定模式运行,但我不知道该怎么做。

任何建议都会有很大帮助。

我已经参考了创建上述 bean的链接。

标签: javaspring-bootliquibasemulti-tenant

解决方案


推荐阅读