• 【mybatis-plus进阶】多租户场景中多数据源自定义来源dynamic-datasource实现


    Springboot+mybatis-plus+dynamic-datasource+Druid 多租户场景中多数据源自定义来源dynamic-datasource实现

    0.前言

    我们低代码平台是支持多租户的模式,用户在平台上配置了多数据源后,数据源会持久化到低代码的数据库中。多租户场景中多数据源自定义来源 是从数据库中通过查询数据源配置信息表,获取到数据库的链接信息和配置信息,然后利用 dynamic-datasource创建多数据源对象注入到容器里。
    在这里插入图片描述

      <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>dynamic-datasource-spring-boot-starterartifactId>
            <version>${dynamic.datasource.version}version>
       dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1. 作者提供了接口

    在这里插入图片描述

    2. 基于此接口的抽象类实现

     @Bean
        public DynamicDataSourceProvider jdbcDynamicDatasourceProvider(DynamicDataSourceProperties properties) {
            // 获取Primary动态数据源
            Map<String, DataSourceProperty> datasourceMap = properties.getDatasource();
            DataSourceProperty masterDsProperty = datasourceMap.get(properties.getPrimary());
            // 从项目配置文件里配置的主数据源中DB加载要多租户的数据源。我们主要用在低代码场景
            return new AbstractJdbcDataSourceProvider(masterDsProperty.getDriverClassName(),
                    masterDsProperty.getUrl(), masterDsProperty.getUsername(), masterDsProperty.getPassword()) {
                @Override
                protected Map<String, DataSourceProperty> executeStmt(Statement statement) {
                    Map<String, DataSourceProperty> dataSourcePropertiesMap = null;
                    ResultSet rs = null;
                    try {
                        dataSourcePropertiesMap = new HashMap<>();
                        // DbConstant.DS_DB_SQL 为查询数据库中配置的多租户的数据源配置信息的SQL如
                        // "SELECT * FROM DATABASE_CONFIG "
                  
                        rs = statement.executeQuery(DbConstant.DS_DB_SQL);
                        while (rs.next()) {
                            DataSourceProperty property = new DataSourceProperty();
                            String databaseCode = rs.getString(DbConstant.DatabaseConfigField.DATABASE_CODE);
                            property.setDriverClassName(rs.getString(DbConstant.DatabaseConfigField.DRIVER_CLASS));
                            property.setUrl(rs.getString(DbConstant.DatabaseConfigField.DATABASE_URL));
                            property.setUsername(rs.getString(DbConstant.DatabaseConfigField.USER_NAME));
                            property.setPassword(AESUtil.decrypt(rs.getString(DbConstant.DatabaseConfigField.USER_PASSWORD)));
                            property.setLazy(DS_DATASOURCE_LAZY);
                            // 设置Druid配置
                            String druidConfig = rs.getString(DbConstant.DatabaseConfigField.DRUID_CONFIG);
                            property.setDruid(getDruidConfig(druidConfig));
                            dataSourcePropertiesMap.put(databaseCode, property);
                        }
                    } catch (SQLException e) {
                        log.error("查询DB数据源配置异常", e);
                    } finally {
                        try {
                            if (rs != null) {
                                rs.close();
                            }
                        } catch (SQLException e) {
                            log.error("数据库ResultSet资源释放异常", e);
                        }
                        try {
                            statement.close();
                        } catch (SQLException e) {
                            log.error("数据库Statement资源释放异常", e);
                        }
                    }
                    log.info(">>>初始化加载DB库中数据源完成");
                    return dataSourcePropertiesMap;
                }
            };
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    自定义负载均衡策略

    如下图slave组下有三个数据源,当用户使用slave切换数据源时会使用负载均衡算法。

    系统自带了两个负载均衡算法

    LoadBalanceDynamicDataSourceStrategy 轮询,是默认的。
    RandomDynamicDataSourceStrategy 随机的。

    spring:
      datasource:
        dynamic:
          datasource:
            master:
              username: sa
              password: ""
              url: jdbc:h2:mem:test
              driver-class-name: org.h2.Driver
              schema: db/schema.sql
            slave_1:
              username: sa
              password: ""
              url: jdbc:h2:mem:test
              driver-class-name: org.h2.Driver
            slave_2:
              username: sa
              password: ""
              url: jdbc:h2:mem:test
              driver-class-name: org.h2.Driver
            slave_3:
              username: sa
              password: ""
              url: jdbc:h2:mem:test
              driver-class-name: org.h2.Driver
          strategy: com.baomidou.dynamic.datasource.strategy.LoadBalanceDynamicDataSourceStrategy
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    如何自定义

    如果默认的两个都不能满足要求,可以参考源码自定义。 暂时只能全局更改。

    import java.util.List;
    import java.util.concurrent.ThreadLocalRandom;
    import javax.sql.DataSource;
    
    public class RandomDynamicDataSourceStrategy implements DynamicDataSourceStrategy {
        public RandomDynamicDataSourceStrategy() {
        }
    
        public DataSource determineDataSource(List<DataSource> dataSources) {
            return (DataSource)dataSources.get(ThreadLocalRandom.current().nextInt(dataSources.size()));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    多数据源懒启动配置

    懒启动:连接池创建出来后并不会立即初始化连接池,等需要使用connection的时候再初始化。

    暂时只支持Druid和HikariCp和BeeCp连接池。

    主要场景可能适合于数据源很多,又不需要启动立即初始化的情况,可以减少系统启动时间。

    缺点在于,如果参数配置有误,则启动的时候不知道,初始化的时候失败,可能一直抛异常。

    配置使用

    spring:
      datasource:
        dynamic:
          primary: master #设置默认的数据源或者数据源组,默认值即为master
          strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
          lazy: true #默认false非懒启动,系统加载到数据源立即初始化连接池
          datasource:
            master:
              url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver
              lazy: true #表示这个数据源懒启动
            db1:
              url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver
            db2:
              url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3. 参考资料

    从3.4.0开始,可以注入多个DynamicDataSourceProvider的Bean以实现同时从多个不同来源加载数据源,注意同名会被覆盖。

    1. dynamic-datasource GitHub 仓库 ↗:dynamic-datasource 的官方 GitHub 仓库,包含源代码、文档和示例等资源。
  • 相关阅读:
    哪些问题会让企业申报不了高新技术企业?
    【分类网络】AlexNet
    Kotlin Files Paths write ByteArray writeString写多行BufferedWriter
    监控——前端性能监控方案
    深入理解树状数组
    闰秒(leapsecond)和原子钟(Atomic Clock)究竟是什么
    TypeScript-02基础知识之函数
    【Mac】Mac与PlayCover、Mac关闭sip
    找实习之从0开始的后端学习日记【9.17】
    请问 前端小程序如何做多端。比如有司机端和用户端。
  • 原文地址:https://blog.csdn.net/wangshuai6707/article/details/132670782