Skip to content

数据库相关的配置全部位于database.yml

yaml
acuity:
  mysql:  # mysql 常用配置
    ip: 127.0.0.1
    port: 3306
    driverClassName: com.mysql.cj.jdbc.Driver
    database: acuity_pro_defaults
    username: 'root'
    password: 'root'

  database:    # 字段介绍参考 DatabaseProperties
    multiTenantType: DATASOURCE
    # COLUMN模式中隔离 租户 的列名
    tenantIdColumn: create_org_id
    initDatabasePrefix:  # DATASOURCE 和 DATASOURCE_COLUMN模式 租户库 前缀
        - acuity_ds_c_base
    ignoreTable: # 在执行sql时,忽略 租户插件自动拼接租户编码的表 (仅 COLUMN 和 DATASOURCE_COLUMN 模式有效)
      - def_xxx
    ignoreTablePrefix:  # 在执行sql时,忽略 租户插件自动拼接租户编码的表 前缀 (仅 COLUMN 和 DATASOURCE_COLUMN 模式有效)
      - def_xxx   

    # 是否不允许写入数据  WriteInterceptor
    isNotWrite: false
    # 是否启用  sql性能规范插件
    isBlockAttack: false
    # 是否启用  sql性能规范插件
    isIllegalSql: false
    # 是否启用分布式事务
    isSeata: false
    # 是否在控制台打印详细的SQL语句  生产环境请设置 p6spy = false
    p6spy: true
    # 是否启用数据权限
    isDataScope: true

    # 分页相关start
    # 分页大小限制
    maxLimit: -1
    # 数据库类型
    dbType: MYSQL
    # 溢出总页数后是否进行处理
    overflow: true
    # 生成 countSql 优化掉 join 现在只支持 left join
    optimizeJoin: true
    # 分页相关end

    # id 生成策略开始
    # id生成策略 支持 HU_TOOL、CACHE、DEFAULTS
    id-type: HU_TOOL
    hutoolId:
      workerId: 0
      dataCenterId: 0
    defaultId:
        time-bits: 31
      worker-bits: 22
      seq-bits: 10
      epochStr: '2020-09-15'
    cache-id:
      time-bits: 31
      worker-bits: 22
      seq-bits: 10
      epochStr: '2020-09-15'
      boost-power: 3
      padding-factor: 50
        # id 生成策略结束

# mysql 通用配置
spring:
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  datasource:
    dynamic:
      enabled: false  # 这里一定要写成false,无论是什么模式
    druid:
      enable: true
      # 从这里开始(druid),中间的这段配置用于 acuity.database.multiTenantType != DATASOURCE 时
      username: ${acuity.mysql.username}
      password: ${acuity.mysql.password}
      driver-class-name: ${acuity.mysql.driverClassName}
      url: jdbc:mysql://${acuity.mysql.ip}:${acuity.mysql.port}/${acuity.mysql.database}?serverTimezone=CTT&characterEncoding=utf8&useUnicode=true&useSSL=false&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&nullCatalogMeansCurrent=true
      db-type: mysql
      initialSize: 10  #初始的数据源链接数
      minIdle: 10   # 最小连接池数量
      maxActive: 200  # 最大连接池数量
      max-wait: 60000   # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
      pool-prepared-statements: true  # 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
      max-pool-prepared-statement-per-connection-size: 20   # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      validation-query: SELECT 'x'   # 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
      test-on-borrow: false  # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
      test-on-return: false  # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
      test-while-idle: true # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      time-between-eviction-runs-millis: 60000  #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      min-evictable-idle-time-millis: 300000    #配置一个连接在池中最小生存的时间,单位是毫秒
      filters: stat,wall     # 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:   监控统计用的filter:stat 、 日志用的filter:log4j  、  防御sql注入的filter:wall
      filter:
        wall:
          enabled: true
          config:
            commentAllow: true
            multiStatementAllow: true
            noneBaseStatementAllow: true
      # 从这里结束(druid),中间的这段配置用于 acuity.database.multiTenantType != DATASOURCE 时

      # 以下的2段配置,同时适用于所有模式
      web-stat-filter:  # WebStatFilter配置,说明请参考Druid Wiki,配置_配置WebStatFilter
        enabled: true
        url-pattern: /*
        exclusions: "*.js , *.gif ,*.jpg ,*.png ,*.css ,*.ico , /druid/*"
        session-stat-max-count: 1000
        profile-enable: true
        session-stat-enable: false
      stat-view-servlet:  #展示Druid的统计信息,StatViewServlet的用途包括:1.提供监控信息展示的html页面2.提供监控信息的JSON API
        enabled: true
        url-pattern: /druid/*   #根据配置中的url-pattern来访问内置监控页面,如果是上面的配置,内置监控页面的首页是/druid/index.html例如:http://127.0.0.1:9000/druid/index.html
        reset-enable: true    #允许清空统计数据
        login-username: acuity
        login-password: acuity
        allow: ''
acuity:
  mysql:  # mysql 常用配置
    ip: 127.0.0.1
    port: 3306
    driverClassName: com.mysql.cj.jdbc.Driver
    database: acuity_pro_defaults
    username: 'root'
    password: 'root'

  database:    # 字段介绍参考 DatabaseProperties
    multiTenantType: DATASOURCE
    # COLUMN模式中隔离 租户 的列名
    tenantIdColumn: create_org_id
    initDatabasePrefix:  # DATASOURCE 和 DATASOURCE_COLUMN模式 租户库 前缀
        - acuity_ds_c_base
    ignoreTable: # 在执行sql时,忽略 租户插件自动拼接租户编码的表 (仅 COLUMN 和 DATASOURCE_COLUMN 模式有效)
      - def_xxx
    ignoreTablePrefix:  # 在执行sql时,忽略 租户插件自动拼接租户编码的表 前缀 (仅 COLUMN 和 DATASOURCE_COLUMN 模式有效)
      - def_xxx   

    # 是否不允许写入数据  WriteInterceptor
    isNotWrite: false
    # 是否启用  sql性能规范插件
    isBlockAttack: false
    # 是否启用  sql性能规范插件
    isIllegalSql: false
    # 是否启用分布式事务
    isSeata: false
    # 是否在控制台打印详细的SQL语句  生产环境请设置 p6spy = false
    p6spy: true
    # 是否启用数据权限
    isDataScope: true

    # 分页相关start
    # 分页大小限制
    maxLimit: -1
    # 数据库类型
    dbType: MYSQL
    # 溢出总页数后是否进行处理
    overflow: true
    # 生成 countSql 优化掉 join 现在只支持 left join
    optimizeJoin: true
    # 分页相关end

    # id 生成策略开始
    # id生成策略 支持 HU_TOOL、CACHE、DEFAULTS
    id-type: HU_TOOL
    hutoolId:
      workerId: 0
      dataCenterId: 0
    defaultId:
        time-bits: 31
      worker-bits: 22
      seq-bits: 10
      epochStr: '2020-09-15'
    cache-id:
      time-bits: 31
      worker-bits: 22
      seq-bits: 10
      epochStr: '2020-09-15'
      boost-power: 3
      padding-factor: 50
        # id 生成策略结束

# mysql 通用配置
spring:
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  datasource:
    dynamic:
      enabled: false  # 这里一定要写成false,无论是什么模式
    druid:
      enable: true
      # 从这里开始(druid),中间的这段配置用于 acuity.database.multiTenantType != DATASOURCE 时
      username: ${acuity.mysql.username}
      password: ${acuity.mysql.password}
      driver-class-name: ${acuity.mysql.driverClassName}
      url: jdbc:mysql://${acuity.mysql.ip}:${acuity.mysql.port}/${acuity.mysql.database}?serverTimezone=CTT&characterEncoding=utf8&useUnicode=true&useSSL=false&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&nullCatalogMeansCurrent=true
      db-type: mysql
      initialSize: 10  #初始的数据源链接数
      minIdle: 10   # 最小连接池数量
      maxActive: 200  # 最大连接池数量
      max-wait: 60000   # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
      pool-prepared-statements: true  # 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
      max-pool-prepared-statement-per-connection-size: 20   # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      validation-query: SELECT 'x'   # 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
      test-on-borrow: false  # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
      test-on-return: false  # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
      test-while-idle: true # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      time-between-eviction-runs-millis: 60000  #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      min-evictable-idle-time-millis: 300000    #配置一个连接在池中最小生存的时间,单位是毫秒
      filters: stat,wall     # 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:   监控统计用的filter:stat 、 日志用的filter:log4j  、  防御sql注入的filter:wall
      filter:
        wall:
          enabled: true
          config:
            commentAllow: true
            multiStatementAllow: true
            noneBaseStatementAllow: true
      # 从这里结束(druid),中间的这段配置用于 acuity.database.multiTenantType != DATASOURCE 时

      # 以下的2段配置,同时适用于所有模式
      web-stat-filter:  # WebStatFilter配置,说明请参考Druid Wiki,配置_配置WebStatFilter
        enabled: true
        url-pattern: /*
        exclusions: "*.js , *.gif ,*.jpg ,*.png ,*.css ,*.ico , /druid/*"
        session-stat-max-count: 1000
        profile-enable: true
        session-stat-enable: false
      stat-view-servlet:  #展示Druid的统计信息,StatViewServlet的用途包括:1.提供监控信息展示的html页面2.提供监控信息的JSON API
        enabled: true
        url-pattern: /druid/*   #根据配置中的url-pattern来访问内置监控页面,如果是上面的配置,内置监控页面的首页是/druid/index.html例如:http://127.0.0.1:9000/druid/index.html
        reset-enable: true    #允许清空统计数据
        login-username: acuity
        login-password: acuity
        allow: ''

禁止在控制台打印sql

yaml
acuity:
  database:
    # 生产环境请设置p6spy = false  
    p6spy: false  
    # p6spy 输出的sql格式是 TenantP6SpyLogger 控制的
acuity:
  database:
    # 生产环境请设置p6spy = false  
    p6spy: false  
    # p6spy 输出的sql格式是 TenantP6SpyLogger 控制的

调整在控制台打印的sql参数或格式

java
public class TenantP6SpyLogger implements MessageFormattingStrategy {
    public static final String REGX = "[\\s]+";

    @Override
    public String formatMessage(int connectionId, String now, long elapsed, String category,
                                String prepared, String sql, String url) {
        return StringUtils.isNotBlank(sql) ?
                StrUtil.format(" tenant: {} userId: {} \n Consume Time:{} ms {} \n url: {} \n Execute SQL:{} \n",
                        ContextUtil.getTenant(), ContextUtil.getUserId(), elapsed, now, url, sql.replaceAll(REGX, StringPool.SPACE)) :
                StringPool.EMPTY;
    }
}
public class TenantP6SpyLogger implements MessageFormattingStrategy {
    public static final String REGX = "[\\s]+";

    @Override
    public String formatMessage(int connectionId, String now, long elapsed, String category,
                                String prepared, String sql, String url) {
        return StringUtils.isNotBlank(sql) ?
                StrUtil.format(" tenant: {} userId: {} \n Consume Time:{} ms {} \n url: {} \n Execute SQL:{} \n",
                        ContextUtil.getTenant(), ContextUtil.getUserId(), elapsed, now, url, sql.replaceAll(REGX, StringPool.SPACE)) :
                StringPool.EMPTY;
    }
}

使用自定义sql时,如何优雅的写模糊查询参数

  • xml中自定义sql:
sql
select * from user where 1=1 
and name like #{keyword, typeHandler=fullLike}     
and account like #{keyword, typeHandler=leftLike}
and describe like #{keyword, typeHandler=rightLike}
select * from user where 1=1 
and name like #{keyword, typeHandler=fullLike}     
and account like #{keyword, typeHandler=leftLike}
and describe like #{keyword, typeHandler=rightLike}
  • 实际sql:
sql
select * from user where 1=1  and name like '%张三%'  and account like '%acuity'   and describe  like '工作%'
select * from user where 1=1  and name like '%张三%'  and account like '%acuity'   and describe  like '工作%'
  • 原理: FullLikeTypeHandler、LeftLikeTypeHandler、RightLikeTypeHandler

id、createTime、createdBy、updateTime、updatedBy 等字段自动填充

  • 实现元对象处理器接口:top.acuity.commons.database.datasource.AcuityMetaObjectHandler

      1. insert 方法,自动填充 id, createdTime, updatedTime, createdBy, updatedBy 字段,字段为NULL则自动填充,不为空则使用传递进来的
      1. update 方法,自动填充 id, updatedTime, updatedBy 字段,字段为NULL则自动填充,不为空则使用传递进来的
  • 注解填充字段@TableField(.. fill = FieldFill.INSERT)生成器策略部分也可以配置!

    java
    public class User {
    
    // 注意!这里需要标记为填充字段
    @TableField(.. fill = FieldFill.INSERT)
    private String fillField;
    
    ....
    }
    public class User {
    
    // 注意!这里需要标记为填充字段
    @TableField(.. fill = FieldFill.INSERT)
    private String fillField;
    
    ....
    }

集群部署如何配置雪花id规则? 为什么id为重复?

yaml
acuity:
  database: 
    id-type: HU_TOOL   # HU_TOOL、 DEFAULT、CACHE
    hutoolId:
      workerId: 0                          
      dataCenterId: 0
    cache-id:
      time-bits: 31
      worker-bits: 22
      seq-bits: 10
      epochStr: '2020-09-15'
      boost-power: 3
      padding-factor: 50
acuity:
  database: 
    id-type: HU_TOOL   # HU_TOOL、 DEFAULT、CACHE
    hutoolId:
      workerId: 0                          
      dataCenterId: 0
    cache-id:
      time-bits: 31
      worker-bits: 22
      seq-bits: 10
      epochStr: '2020-09-15'
      boost-power: 3
      padding-factor: 50

雪花id生成类型有3种: HU_TOOL、DEFAULT、CACHE

  • HU_TOOL: 使用hutool 提供的雪花生成算法,集群部署时,需要保证每个节点读取到的 acuity.database.hutoolId.workerIdacuity.database.hutoolId.dataCenterId 不一样,否则可能会造成id重复问题
  • DEFAULT:UidGenerator通过借用未来时间来解决sequence天然存在的并发限制。
  • CACHE( 推荐使用 ):采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费, 同时对CacheLine补齐,避免了由RingBuffer带来的硬件级「伪共享」问题 。

其中 DEFAULT、CACHE 依赖于 worker_node 表, 工作原理是: 每次启动项目时,自增一条数据,使用自增id 作为雪花id的 workerId ,避免了集群部署时,动态扩展和缩减节点,需要手动配置不同的workerId 的问题。 而CACHE模式,是在DEFAULT模式的基础上做了缓存,性能更好。 所以集群部署,尤其是需要动态扩容时,强烈建议使用 CACHE 模式。

如何在 SuperMapper 中,增加方法

  1. 在SuperMapper中定义方法接口,如:

    java
    /**
         * 全量修改所有字段
         *
         * @param entity 实体
         * @return 修改数量
       */
    int updateAllById(@Param(Constants.ENTITY) T entity);
    
    /**
       * 批量插入所有字段
       * <p>
       * 只测试过MySQL!只测试过MySQL!只测试过MySQL!
       *
       * @param entityList 实体集合
       * @return 插入数量
       */
    int insertBatchSomeColumn(List<T> entityList);
    /**
         * 全量修改所有字段
         *
         * @param entity 实体
         * @return 修改数量
       */
    int updateAllById(@Param(Constants.ENTITY) T entity);
    
    /**
       * 批量插入所有字段
       * <p>
       * 只测试过MySQL!只测试过MySQL!只测试过MySQL!
       *
       * @param entityList 实体集合
       * @return 插入数量
       */
    int insertBatchSomeColumn(List<T> entityList);
  2. 定义 updateAllById 方法的动态sql. (具体的实现代码位于: AlwaysUpdateSomeColumnById )

    java
    @NoArgsConstructor
     public class UpdateAllById extends AlwaysUpdateSomeColumnById {
    
         public UpdateAllById(Predicate<TableFieldInfo> predicate) {
             super(predicate);
         }
    
         @Override
         public String getMethod(SqlMethod sqlMethod) {
             // 自定义 mapper 方法名
             return "updateAllById";
         }
     }
    @NoArgsConstructor
     public class UpdateAllById extends AlwaysUpdateSomeColumnById {
    
         public UpdateAllById(Predicate<TableFieldInfo> predicate) {
             super(predicate);
         }
    
         @Override
         public String getMethod(SqlMethod sqlMethod) {
             // 自定义 mapper 方法名
             return "updateAllById";
         }
     }
  3. 注入 UpdateAllById

    java
    public class AcuitySqlInjector extends DefaultSqlInjector {
        @Override
        public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
            List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
             methodList.add(new UpdateAllById(field -> !ArrayUtil.containsAny(new String[]{
                     SuperEntity.CREATE_TIME_COLUMN, SuperEntity.CREATED_BY_COLUMN
             }, field.getColumn())));
             return methodList;
         }
     }
    public class AcuitySqlInjector extends DefaultSqlInjector {
        @Override
        public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
            List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
             methodList.add(new UpdateAllById(field -> !ArrayUtil.containsAny(new String[]{
                     SuperEntity.CREATE_TIME_COLUMN, SuperEntity.CREATED_BY_COLUMN
             }, field.getColumn())));
             return methodList;
         }
     }
  4. 配置 AcuitySqlInjector

    java
     @Bean
     @ConditionalOnMissingBean
     public AcuitySqlInjector getMySqlInjector() {
         return new AcuitySqlInjector();
     }
     @Bean
     @ConditionalOnMissingBean
     public AcuitySqlInjector getMySqlInjector() {
         return new AcuitySqlInjector();
     }

如何修改datasource模式的 租户数据库名

  1. 修改 database.yml

    yaml
    acuity:
      mysql: 
        database: test_defaults   # 启动时连接的默认库
      database:
        initDatabasePrefix: 
          - test_base   # 启动时连接的租户库前缀  务必与 ContextConstants.TENANT_BASE_POOL_NAME_HEADER 保持一致
    acuity:
      mysql: 
        database: test_defaults   # 启动时连接的默认库
      database:
        initDatabasePrefix: 
          - test_base   # 启动时连接的租户库前缀  务必与 ContextConstants.TENANT_BASE_POOL_NAME_HEADER 保持一致
  2. 修改 ContextConstants

    java
    public final class ContextConstants {
        // 切换数据源时租户前缀   务必与 acuity.database.initDatabasePrefix 保持一致
        public static final String TENANT_BASE_POOL_NAME_HEADER = "test_base";
    }
    public final class ContextConstants {
        // 切换数据源时租户前缀   务必与 acuity.database.initDatabasePrefix 保持一致
        public static final String TENANT_BASE_POOL_NAME_HEADER = "test_base";
    }

服务运行时,如何链接、查询多个租户库?

  1. 修改 database.yml
yaml
acuity:
  database:
    initDatabasePrefix: 
      - test_base    # 启动时连接的租户库前缀  务必与TENANT_BASE_POOL_NAME_HEADER保持一致
      - test_extend  # 启动时连接的租户库前缀2 务必与TENANT_EXTEND_POOL_NAME_HEADER保持一致  
      - test_xxx     # 启动时连接的租户库前缀3  需要在ContextConstants中新增一个常量:  TENANT_XXX_POOL_NAME_HEADER
acuity:
  database:
    initDatabasePrefix: 
      - test_base    # 启动时连接的租户库前缀  务必与TENANT_BASE_POOL_NAME_HEADER保持一致
      - test_extend  # 启动时连接的租户库前缀2 务必与TENANT_EXTEND_POOL_NAME_HEADER保持一致  
      - test_xxx     # 启动时连接的租户库前缀3  需要在ContextConstants中新增一个常量:  TENANT_XXX_POOL_NAME_HEADER
  1. 修改 ContextConstants
java
public final class ContextConstants {
    // (内置)切换数据源时租户前缀
    public static final String TENANT_BASE_POOL_NAME_HEADER = "test_base";
// (内置)
    public static final String TENANT_EXTEND_POOL_NAME_HEADER = "test_extend";
// (自行新增) 
    public static final String TENANT_XXX_POOL_NAME_HEADER = "test_xxx";
}
public final class ContextConstants {
    // (内置)切换数据源时租户前缀
    public static final String TENANT_BASE_POOL_NAME_HEADER = "test_base";
// (内置)
    public static final String TENANT_EXTEND_POOL_NAME_HEADER = "test_extend";
// (自行新增) 
    public static final String TENANT_XXX_POOL_NAME_HEADER = "test_xxx";
}
  1. 修改 ContextUtil
java
public final class ContextUtil {

    public static void setTenantId(Object tenantId) {
        set(ContextConstants.TENANT_ID_HEADER, tenantId);
        setTenantBasePoolName(tenantId);
        setTenantExtendPoolName(tenantId);
        // 自行新增
        setTenantXxxPoolName(tenantId);
    }

    /**
     * 切换xxx库
     *
     * @param tenantId
     */
    public static void setTenantXxxPoolName(Object tenantId) {
        set(ContextConstants.TENANT_XXX_POOL_NAME_HEADER, tenantId);
    }
}
public final class ContextUtil {

    public static void setTenantId(Object tenantId) {
        set(ContextConstants.TENANT_ID_HEADER, tenantId);
        setTenantBasePoolName(tenantId);
        setTenantExtendPoolName(tenantId);
        // 自行新增
        setTenantXxxPoolName(tenantId);
    }

    /**
     * 切换xxx库
     *
     * @param tenantId
     */
    public static void setTenantXxxPoolName(Object tenantId) {
        set(ContextConstants.TENANT_XXX_POOL_NAME_HEADER, tenantId);
    }
}
  1. 修改 DsConstant
java
public interface DsConstant {
    /**
     * 默认数据源
     */
    String DEFAULTS = "0";
    /**
     * 动态租户数据源
     */
    String BASE_TENANT = "#thread." + TENANT_BASE_POOL_NAME_HEADER;
    String EXTEND_TENANT = "#thread." + TENANT_EXTEND_POOL_NAME_HEADER;
    // 自行新增
    String XXX_TENANT = "#thread." + ContextConstants.TENANT_XXX_POOL_NAME_HEADER;
}
public interface DsConstant {
    /**
     * 默认数据源
     */
    String DEFAULTS = "0";
    /**
     * 动态租户数据源
     */
    String BASE_TENANT = "#thread." + TENANT_BASE_POOL_NAME_HEADER;
    String EXTEND_TENANT = "#thread." + TENANT_EXTEND_POOL_NAME_HEADER;
    // 自行新增
    String XXX_TENANT = "#thread." + ContextConstants.TENANT_XXX_POOL_NAME_HEADER;
}
  1. 编写ServiceImpl代码
java
@DS(DsConstant.BASE_TENANT)
public class ServiceImpl {
    public void test1() {
        // CRUD 操作 test_base 库
    }

    @DS(DsConstant.DEFAULTS)
    public void test2() {
        // CRUD 操作 test_defaults 库
    }
    @DS(DsConstant.EXTEND_TENANT)
    public void test3() {
        // CRUD 操作 test_extend 库
    }
    @DS(DsConstant.XXX_TENANT)
    public void test4() {
        // CRUD 操作 test_xxx 库
    }

}
@DS(DsConstant.BASE_TENANT)
public class ServiceImpl {
    public void test1() {
        // CRUD 操作 test_base 库
    }

    @DS(DsConstant.DEFAULTS)
    public void test2() {
        // CRUD 操作 test_defaults 库
    }
    @DS(DsConstant.EXTEND_TENANT)
    public void test3() {
        // CRUD 操作 test_extend 库
    }
    @DS(DsConstant.XXX_TENANT)
    public void test4() {
        // CRUD 操作 test_xxx 库
    }

}

欢迎使用天源云Saas快速开发系统