Springboot 多数据源 dynamic-datasource动态添加移除数据源

文章目录

  • 0.前言
  • 1. 动态添加移除数据源
  • 2.基础介绍
  • 3. 使用步骤示例
    • 简单示例,自己手动构造dataSource
    • 复杂示例,使用DataSourceCreator构造dataSource
    • 4. 示例项目
    • 5. 源码分析

      0.前言

      上一篇文章我们讲了如何通过多数据源组件,在Spring boot Druid 连接池项目中配置多数据源,并且通过@DS注解的方式切换数据源,《Spring Boot 配置多数据源【最简单的方式】》。但是在多租户的业务场景中,我们通常需要手动的切换数据源,那么本文将解答你的额疑惑。

      1. 动态添加移除数据源

      dynamic-datasource是一款基于Spring Boot 动态数据源框架,在应用程序运行时可以动态添加、移除数据源的功能。

      2.基础介绍

      本文我们还是以dynamic-datasource来进阶学习。提供了一系列的API和配置项,可以非常方便地实现动态添加、移除数据源的功能。本文将介绍如何使用dynamic-datasource动态添加、移除数据源,并对相关代码进行解析。在多租户应用、读写分离等场景下,动态数据源可以方便地实现数据源的动态切换,提高应用程序的灵活性和扩展性。

      主要在多租户场景中,常常新的一个租户进来需要动态的添加一个数据源到库中,使得系统不用重启即可切换数据源。

      3. 使用步骤示例

      dynamic-datasource 3.4.0及以上版本和老版本注入方式有一定差别,根据自己版本注入。

      注意一定要让多数据源使用 @Primary ,让其成为主数据源。

      简单示例,自己手动构造dataSource

      //3.4.0版本以下
      @Primary
      @Bean
      public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) { DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
          dataSource.setPrimary(properties.getPrimary());
          dataSource.setStrict(properties.getStrict());
          dataSource.setStrategy(properties.getStrategy());
          dataSource.setProvider(dynamicDataSourceProvider);
          dataSource.setP6spy(properties.getP6spy());
          dataSource.setSeata(properties.getSeata());
          return dataSource;
      }
      //3.4.0版本及以上
      @Primary
      @Bean
      public DataSource dataSource() { DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
          dataSource.setPrimary(properties.getPrimary());
          dataSource.setStrict(properties.getStrict());
          dataSource.setStrategy(properties.getStrategy());
          dataSource.setP6spy(properties.getP6spy());
          dataSource.setSeata(properties.getSeata());
          return dataSource;
      }
      

      主要变更是因为3.4.0支持了多个provider同时生效,采取了内部注入。 源码改动参考

      https://github.com/baomidou/dynamic-datasource-spring-boot-starter/commit/6e8d2954499f83269302d23b58f8832c31e07ef7

      复杂示例,使用DataSourceCreator构造dataSource

      本文我们还是以dynamic-datasource来进阶学习。提供了一系列的API和配置项,可以非常方便地实现动态添加、移除数据源的功能。本文将介绍如何使用dynamic-datasource动态添加、移除数据源,并对相关代码进行解析。在多租户应用、读写分离等场景下,动态数据源可以方便地实现数据源的动态切换,提高应用程序的灵活性和扩展性。

      主要在多租户场景中,常常新的一个租户进来需要动态的添加一个数据源到库中,使得系统不用重启即可切换数据源。

      import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
      import com.baomidou.dynamic.datasource.creator.*;
      import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
      import com.baomidou.samples.ds.dto.DataSourceDTO;
      import io.swagger.annotations.Api;
      import io.swagger.annotations.ApiOperation;
      import org.springframework.beans.BeanUtils;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.validation.annotation.Validated;
      import org.springframework.web.bind.annotation.*;
      import javax.sql.DataSource;
      import java.util.Set;
      @RestController
      @RequestMapping("/datasources")
      @Api(tags = "添加删除数据源")
      public class DataSourceController { @Autowired
          private DataSource dataSource;
         // private final DataSourceCreator dataSourceCreator; //3.3.1及以下版本使用这个通用,强烈推荐sb2用户至少升级到3.5.2版本
          @Autowired
          private DefaultDataSourceCreator dataSourceCreator;
      //如果是用4.x以上版本,因为要和spring解绑,重构了一些东西,比如缺少了懒启动和启动初始化数据库。不太建议用以下独立的创建器,只建议用上面的DefaultDataSourceCreator 
          @Autowired
          private BasicDataSourceCreator basicDataSourceCreator;
          @Autowired
          private JndiDataSourceCreator jndiDataSourceCreator;
          @Autowired
          private DruidDataSourceCreator druidDataSourceCreator;
          @Autowired
          private HikariDataSourceCreator hikariDataSourceCreator;
          @Autowired
          private BeeCpDataSourceCreator beeCpDataSourceCreator;
          @Autowired
          private Dbcp2DataSourceCreator dbcp2DataSourceCreator;
          @GetMapping
          @ApiOperation("获取当前所有数据源")
          public Set now() { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
              return ds.getDataSources().keySet();
          }
          //通用数据源会根据maven中配置的连接池根据顺序依次选择。
          //默认的顺序为druid>hikaricp>beecp>dbcp>spring basic
          @PostMapping("/add")
          @ApiOperation("通用添加数据源(推荐)")
          public Set add(@Validated @RequestBody DataSourceDTO dto) { DataSourceProperty dataSourceProperty = new DataSourceProperty();
              BeanUtils.copyProperties(dto, dataSourceProperty);
              DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
              DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
              ds.addDataSource(dto.getPoolName(), dataSource);
              return ds.getDataSources().keySet();
          }
          @PostMapping("/addBasic(强烈不推荐,除了用了马上移除)")
          @ApiOperation(value = "添加基础数据源", notes = "调用Springboot内置方法创建数据源,兼容1,2")
          public Set addBasic(@Validated @RequestBody DataSourceDTO dto) { DataSourceProperty dataSourceProperty = new DataSourceProperty();
              BeanUtils.copyProperties(dto, dataSourceProperty);
              DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
              DataSource dataSource = basicDataSourceCreator.createDataSource(dataSourceProperty);
              ds.addDataSource(dto.getPoolName(), dataSource);
              return ds.getDataSources().keySet();
          }
          @PostMapping("/addJndi")
          @ApiOperation("添加JNDI数据源")
          public Set addJndi(String pollName, String jndiName) { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
              DataSource dataSource = jndiDataSourceCreator.createDataSource(jndiName);
              ds.addDataSource(poolName, dataSource);
              return ds.getDataSources().keySet();
          }
          @PostMapping("/addDruid")
          @ApiOperation("基础Druid数据源")
          public Set addDruid(@Validated @RequestBody DataSourceDTO dto) { DataSourceProperty dataSourceProperty = new DataSourceProperty();
              BeanUtils.copyProperties(dto, dataSourceProperty);
              dataSourceProperty.setLazy(true);
              DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
              DataSource dataSource = druidDataSourceCreator.createDataSource(dataSourceProperty);
              ds.addDataSource(dto.getPoolName(), dataSource);
              return ds.getDataSources().keySet();
          }
          @PostMapping("/addHikariCP")
          @ApiOperation("基础HikariCP数据源")
          public Set addHikariCP(@Validated @RequestBody DataSourceDTO dto) { DataSourceProperty dataSourceProperty = new DataSourceProperty();
              BeanUtils.copyProperties(dto, dataSourceProperty);
              dataSourceProperty.setLazy(true);//3.4.0版本以下如果有此属性,需手动设置,不然会空指针。
              DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
              DataSource dataSource = hikariDataSourceCreator.createDataSource(dataSourceProperty);
              ds.addDataSource(dto.getPoolName(), dataSource);
              return ds.getDataSources().keySet();
          }
          @PostMapping("/addBeeCp")
          @ApiOperation("基础BeeCp数据源")
          public Set addBeeCp(@Validated @RequestBody DataSourceDTO dto) { DataSourceProperty dataSourceProperty = new DataSourceProperty();
              BeanUtils.copyProperties(dto, dataSourceProperty);
              dataSourceProperty.setLazy(true);//3.4.0版本以下如果有此属性,需手动设置,不然会空指针。
              DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
              DataSource dataSource = beeCpDataSourceCreator.createDataSource(dataSourceProperty);
              ds.addDataSource(dto.getPoolName(), dataSource);
              return ds.getDataSources().keySet();
          }
          @PostMapping("/addDbcp")
          @ApiOperation("基础Dbcp数据源")
          public Set addDbcp(@Validated @RequestBody DataSourceDTO dto) { DataSourceProperty dataSourceProperty = new DataSourceProperty();
              BeanUtils.copyProperties(dto, dataSourceProperty);
              dataSourceProperty.setLazy(true);//3.4.0版本以下如果有此属性,需手动设置,不然会空指针。
              DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
              DataSource dataSource = dbcp2DataSourceCreator.createDataSource(dataSourceProperty);
              ds.addDataSource(dto.getPoolName(), dataSource);
              return ds.getDataSources().keySet();
          }
          @DeleteMapping
          @ApiOperation("删除数据源")
          public String remove(String name) { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
              ds.removeDataSource(name);
              return "删除成功";
          }
      }
      

      4. 示例项目

      https://github.com/dynamic-datasource/dynamic-datasource-samples/tree/master/features-samples/add-remove-datasource-sample

      5. 源码分析

      public interface DataSourceCreator { /**
           * 通过属性创建数据源
           *
           * @param dataSourceProperty 数据源属性
           * @return 被创建的数据源
           */
          DataSource createDataSource(DataSourceProperty dataSourceProperty);
          /**
           * 当前创建器是否支持根据此属性创建
           *
           * @param dataSourceProperty 数据源属性
           * @return 是否支持
           */
          boolean support(DataSourceProperty dataSourceProperty);
      }
      

      DataSourceCreator是一个接口,定义了根据参数创建数据源的接口。

      其他creator实现此接口,本项目暂时实现了Druid和Hikaricp的等连接池的实现。

      BasicDataSourceCreator 是调用Spring原生的创建方式,只支持最最原始的基础配置。

      DefaultDataSourceCreator 是一个通用的创建器,其根据环境自动选择连接池,