0


基于dynamic-datasource实现多租户动态切换数据源

基于dynamic-datasource实现多租户动态切换数据源

一、添加pom配置

<!-- 动态切换数据源 --><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.1</version></dependency>

二、添加yaml配置

spring:datasource:dynamic:primary: master #设置默认的数据源或者数据源组,默认值即为masterstrict:false#设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.datasource:# 主库数据源master:url:username:password:driver-class-name: com.mysql.cj.jdbc.Driver
                # 从库数据源#                slave:#                    # 从数据源开关/默认关闭#                    enabled: false#                    url:#                    username:#                    password:

如果想整合druid连接池,可做如下配置

spring:autoconfigure:exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
    datasource:dynamic:primary: master #设置默认的数据源或者数据源组,默认值即为masterstrict:false#设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.datasource:# 主库数据源master:url:username:password:driver-class-name: com.mysql.cj.jdbc.Driver
                # 从库数据源#                slave:#                    # 从数据源开关/默认关闭#                    enabled: false#                    url:#                    username:#                    password:druid:# 初始连接数initialSize:5# 最小连接池数量minIdle:10# 最大连接池数量maxActive:20# 配置获取连接等待超时的时间maxWait:60000# 配置连接超时时间connectTimeout:30000# 配置网络超时时间socketTimeout:60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis:60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis:300000# 配置一个连接在池中最大生存的时间,单位是毫秒maxEvictableIdleTimeMillis:900000# 配置检测连接是否有效validationQuery: SELECT 1 FROM DUAL
                testWhileIdle:truetestOnBorrow:falsetestOnReturn:falsewebStatFilter:enabled:truestatViewServlet:enabled:true# 设置白名单,不填则允许所有访问allow:url-pattern: /druid/*
                    # 控制台管理用户名和密码login-username: ruoyi
                    login-password:123456filter:stat:enabled:true# 慢SQL记录log-slow-sql:trueslow-sql-millis:1000merge-sql:truewall:config:multi-statement-allow:true

三、创建数据源表,储存数据源信息

建表语句如下

SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS =0;-- ------------------------------ Table structure for sys_database-- ----------------------------DROPTABLEIFEXISTS`sys_database`;CREATETABLE`sys_database`(`database_id`bigint(20)NOTNULLAUTO_INCREMENTCOMMENT'主键',`pool_name`varchar(255)CHARACTERSET utf8mb4 COLLATE utf8mb4_general_ci NULLDEFAULTNULLCOMMENT'连接池名称',`driver_class_name`varchar(255)CHARACTERSET utf8mb4 COLLATE utf8mb4_general_ci NULLDEFAULTNULLCOMMENT'驱动名',`url`varchar(255)CHARACTERSET utf8mb4 COLLATE utf8mb4_general_ci NULLDEFAULTNULLCOMMENT'数据库地址',`username`varchar(255)CHARACTERSET utf8mb4 COLLATE utf8mb4_general_ci NULLDEFAULTNULLCOMMENT'数据库用户名',`password`varchar(255)CHARACTERSET utf8mb4 COLLATE utf8mb4_general_ci NULLDEFAULTNULLCOMMENT'数据库密码',PRIMARYKEY(`database_id`)USINGBTREE)ENGINE=InnoDBAUTO_INCREMENT=3CHARACTERSET= utf8mb4 COLLATE= utf8mb4_general_ci ROW_FORMAT = DYNAMIC;SET FOREIGN_KEY_CHECKS =1;

数据源实体类如下

packagecom.ruoyi.common.core.domain.entity;importorg.apache.commons.lang3.builder.ToStringBuilder;importorg.apache.commons.lang3.builder.ToStringStyle;importcom.ruoyi.common.annotation.Excel;importcom.ruoyi.common.core.domain.BaseEntity;/**
 * 数据库管理对象 sys_database
 * 
 * @author ruoyi
 * @date 2023-03-01
 */publicclassSysDatabaseextendsBaseEntity{privatestaticfinallong serialVersionUID =1L;/** 主键 */privateLong databaseId;/** 连接池名称 */@Excel(name ="连接池名称")privateString poolName;/** 驱动名 */@Excel(name ="驱动名")privateString driverClassName;/** 数据库地址 */@Excel(name ="数据库地址")privateString url;/** 数据库用户名 */@Excel(name ="数据库用户名")privateString username;/** 数据库密码 */@Excel(name ="数据库密码")privateString password;publicvoidsetDatabaseId(Long databaseId){this.databaseId = databaseId;}publicLonggetDatabaseId(){return databaseId;}publicvoidsetPoolName(String poolName){this.poolName = poolName;}publicStringgetPoolName(){return poolName;}publicvoidsetDriverClassName(String driverClassName){this.driverClassName = driverClassName;}publicStringgetDriverClassName(){return driverClassName;}publicvoidsetUrl(String url){this.url = url;}publicStringgetUrl(){return url;}publicvoidsetUsername(String username){this.username = username;}publicStringgetUsername(){return username;}publicvoidsetPassword(String password){this.password = password;}publicStringgetPassword(){return password;}@OverridepublicStringtoString(){returnnewToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE).append("databaseId",getDatabaseId()).append("poolName",getPoolName()).append("driverClassName",getDriverClassName()).append("url",getUrl()).append("username",getUsername()).append("password",getPassword()).toString();}}

四、dynamic-datasource基础操作

(1)查看数据源

importjavax.sql.DataSource;importcom.baomidou.dynamic.datasource.DynamicRoutingDataSource;@AutowiredprivateDataSource dataSource;//这是官方示例,直接返回PoolName(就是yaml配置中的数据源名称)的Set@GetMapping@ApiOperation("获取当前所有数据源")publicSet<String>now(){DynamicRoutingDataSource ds =(DynamicRoutingDataSource) dataSource;return ds.getDataSources().keySet();}//如果只是想知道具体的数据源,输出查看PoolName即可,如下publicvoidgetDataSources(){DynamicRoutingDataSource ds =(DynamicRoutingDataSource) dataSource;for(String poolName : ds.getDataSources().keySet()){System.out.println("poolName:"+ poolName);}}

(2)添加数据源

importcom.ruoyi.system.service.ISysDatabaseService;importjavax.sql.DataSource;importcom.baomidou.dynamic.datasource.DynamicRoutingDataSource;importorg.springframework.beans.BeanUtils;@AutowiredprivateISysDatabaseService sysDatabaseService;@AutowiredprivateDataSource dataSource;//通用数据源会根据maven中配置的连接池根据顺序依次选择。//默认的顺序为druid>hikaricp>beecp>dbcp>spring basic@PostMapping("/add")@ApiOperation("通用添加数据源(推荐)")publicSet<String>add(@Validated@RequestBodyDataSourceDTO dto){DataSourceProperty dataSourceProperty =newDataSourceProperty();// 这里主要是将dto的属性赋值给dataSourceProperty//所以dataSourceProperty中必要的参数,dto都要提供BeanUtils.copyProperties(dto, dataSourceProperty);DynamicRoutingDataSource ds =(DynamicRoutingDataSource) dataSource;DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);// PoolName就是我们yaml配置中说的数据源名称
    ds.addDataSource(dto.getPoolName(), dataSource);return ds.getDataSources().keySet();}

(3)更新数据源

DynamicRoutingDataSource中记录数据源是以map形式,dataSourceMap的k就是poolName,所以如果想要更改poolName对应的数据源,直接覆盖同k的value即可。或者更为保险的做法可以先删除poolName对应的数据源,后续再次添加同名poolName数据源

(4)删除数据源

importcom.ruoyi.system.service.ISysDatabaseService;importjavax.sql.DataSource;importcom.baomidou.dynamic.datasource.DynamicRoutingDataSource;@AutowiredprivateDataSource dataSource;@DeleteMapping@ApiOperation("删除数据源")publicStringremove(String poolName){DynamicRoutingDataSource ds =(DynamicRoutingDataSource) dataSource;
    ds.removeDataSource(poolName);return"删除成功";}

(5)切换数据源

importcom.baomidou.dynamic.datasource.DynamicRoutingDataSource;publicstaticvoidswitchDataSource(String poolName){//需要注意的是手动切换的数据源,最好自己在合适的位置//调用DynamicDataSourceContextHolder.clear()清空当前线程的数据源信息。DynamicDataSourceContextHolder.clear();//切换到对应poolName的数据源DynamicDataSourceContextHolder.push(poolName);}

编写一个拦截器,根据用户,选择对应的数据源

packagecom.ruoyi.framework.interceptor;importcom.baomidou.dynamic.datasource.DynamicRoutingDataSource;importcom.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;importcom.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;importcom.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;importcom.ruoyi.common.core.domain.entity.SysDatabase;importcom.ruoyi.common.core.domain.entity.SysUser;importcom.ruoyi.common.utils.ShiroUtils;importorg.springframework.beans.BeanUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.servlet.HandlerInterceptor;importorg.springframework.web.servlet.ModelAndView;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.sql.DataSource;importjava.util.Objects;publicclassDynamicDatasourceInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler){SysUser sysUser =ShiroUtils.getSysUser();//获取当前登录用户信息if(Objects.nonNull(sysUser)&&Objects.nonNull(sysUser.getDatabase())){SysDatabase database = sysUser.getDatabase();//获取用户对应的数据源信息DynamicDataSourceContextHolder.push(database.getPoolName());//切换数据源returntrue;}returntrue;}@OverridepublicvoidpostHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView){}//    @Override//    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {//        DynamicDataSourceContextHolder.clear();//    }}

将拦截器,注入到spring

packagecom.ruoyi.framework.config;importcom.ruoyi.framework.interceptor.DynamicDatasourceInterceptor;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;@ConfigurationpublicclassDynamicDatasourceInterceptorConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){String[] path ={"/system/**"};// 如果拦截全部可以设置为 /**String[] excludePath ={};// 不需要拦截的接口路径DynamicDatasourceInterceptor dynamicDatasourceInterceptor =newDynamicDatasourceInterceptor();

        registry.addInterceptor(dynamicDatasourceInterceptor).addPathPatterns(path).excludePathPatterns(excludePath);}}

五、springboot启动时,加载所有数据源表中的数据源

packagecom.ruoyi.framework.shiro.service;importcom.baomidou.dynamic.datasource.DynamicRoutingDataSource;importcom.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;importcom.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;importcom.ruoyi.common.config.datasource.DynamicDataSourceContextHolder;importcom.ruoyi.common.core.domain.entity.SysDatabase;importcom.ruoyi.system.service.ISysDatabaseService;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.BeanUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importjavax.annotation.PostConstruct;importjavax.sql.DataSource;importjava.util.List;@ServicepublicclassInjectionAllDatasourceService{publicstaticfinalLogger log =LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);@AutowiredprivateDataSource dataSource;@AutowiredprivateDefaultDataSourceCreator dataSourceCreator;@AutowiredprivateISysDatabaseService databaseService;@PostConstructpublicvoidinit(){
        log.info("项目启动中,加载用户数据");List<SysDatabase> sysDatabases = databaseService.selectSysDatabaseList(newSysDatabase());for(SysDatabase database:sysDatabases){try{DataSourceProperty dataSourceProperty =newDataSourceProperty();// 这里主要是将dto的属性赋值给dataSourceProperty//所以dataSourceProperty中必要的参数,dto都要提供BeanUtils.copyProperties(database, dataSourceProperty);DynamicRoutingDataSource ds =(DynamicRoutingDataSource) dataSource;DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);// PoolName就是我们yaml配置中说的数据源名称
                ds.addDataSource(database.getPoolName(), dataSource);}catch(Exception e){
                e.printStackTrace();}}

        log.info("项目启动中,加载用户数据完成");}}
标签: spring boot java git

本文转载自: https://blog.csdn.net/zznnniuu/article/details/129291674
版权归原作者 zznnniuu 所有, 如有侵权,请联系我们删除。

“基于dynamic-datasource实现多租户动态切换数据源”的评论:

还没有评论