springboot 根据用户ID切换动态数据源代码实现

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介: springboot 根据用户ID切换动态数据源代码实现

首先在application.yml 文件添加一下配置

#每个库可连接最大用户数
dynamic-server:
  #每个服务最大建库数
  database-max-number: 30
  #每个库最大用户连接数
  user-max-number: 200
  template: gis_template

image.gif

gis_template 是数据库模板,就是一个只有表结构的数据库,后边随着用户数的增加,用户数超过每个库的最大用户数的时候,就会根据gis_template自动创建一个新的用户库。

然后项目中添加 dynamicds 模块的代码,仅展示模块文件目录,代码太多。进站时部分代码

image.gif编辑

数据源配置类

import org.springblade.gis.dynamicds.interceptor.DynamicDataSourceInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class DynamicDataSourceConfiguration implements WebMvcConfigurer {
    @Bean
    public DynamicDataSourceInterceptor dynamicDataSourceInterceptor(){
        return new DynamicDataSourceInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        //数据源拦截
        registry.addInterceptor(dynamicDataSourceInterceptor()).addPathPatterns("/**").order(-99);
    }
}

image.gif

动态数据源拦截器

根据token 获取用户id 再根据用户id切换对应数据源

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.gis.dynamicds.cache.DynamicDataSourceCache;
import org.springblade.gis.dynamicds.datasource.MyDynamicDataSource;
import org.springblade.gis.dynamicds.service.DynamicDataSourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * file:DynamicDataSourceInterceptor
 * <p>
 * 文件简要说明
 *
 * @author 2021-10-28 tarzan 创建初始版本
 * @version V1.0  简要版本说明
 */
public class DynamicDataSourceInterceptor implements HandlerInterceptor {
    private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceInterceptor.class);
    @Autowired
    private MyDynamicDataSource dynamicDataSource;
    @Autowired
    private DynamicDataSourceCache dynamicDataSourceCache;
    @Autowired
    private DynamicDataSourceService dynamicDataSourceService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  {
        //获取当前登录用户信息
        BladeUser user = AuthUtil.getUser();
        if(user != null && user.getUserId() != null){
            //如果未获取到 dsName 重新加载数据库
            if(!dynamicDataSourceCache.hasDataSourceName(user.getUserId())){
                dynamicDataSourceService.addUserDataSource(user.getUserId());
            }
            String dsName = dynamicDataSourceCache.getUserIdDataSourceName(user.getUserId());
            if(!dynamicDataSource.switchDataSource(dsName)){
                //如果切换数据源失败 返回错误
                throw new RuntimeException("未找到用户数据库");
            }
            log.info("数据源切换--------------用户名-----"+user.getUserName()+"------------>【{}】", dsName);
        }else{
            log.info("数据源切换------------------------------->默认数据源");
            dynamicDataSource.switchDefaultDataSource();
        }
        return true;
    }
}

image.gif

数据库设计

dynamicDataSource:
  default:
    url: jdbc:postgresql://${POSTGRES_HOST:172.16.10.201}:${POSTGRES_PORT:5432}/${POSTGRES_DATABASE:gis_db}
    username: ${POSTGRES_USERNAME:postgres}
    password: ${POSTGRES_PASSWORD:postgres}
    driverClassName: org.postgresql.Driver
    pool:
      #最小空闲连接
      minimum-idle: 2
      #最大连接
      maximum-pool-size: 3
      # 空闲连接存活最大时间,默认600000(10分钟)
      idle-timeout: 1200000
      # 据库连接超时时间,默认30秒
      connection-timeout: 300000

image.gif

初始链接一个基础数据库,放置用户表,数据源表,数据库表

数据库表建表语句

CREATE TABLE "public"."data_server_database" (
  "id" int8 NOT NULL,
  "data_source_key" varchar(128) COLLATE "pg_catalog"."default" NOT NULL,
  "database_name" varchar(128) COLLATE "pg_catalog"."default" NOT NULL,
  "source_id" int8 NOT NULL,
  "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  "priority" int4 NOT NULL,
  "amount" int4 NOT NULL DEFAULT 0,
  "status" int2 NOT NULL DEFAULT 2
)
;
COMMENT ON COLUMN "public"."data_server_database"."id" IS '主键';
COMMENT ON COLUMN "public"."data_server_database"."data_source_key" IS '数据源连接唯一key';
COMMENT ON COLUMN "public"."data_server_database"."database_name" IS '数据库名';
COMMENT ON COLUMN "public"."data_server_database"."source_id" IS '数据源id(data_server_source表主键id)';
COMMENT ON COLUMN "public"."data_server_database"."create_time" IS '创建时间';
COMMENT ON COLUMN "public"."data_server_database"."update_time" IS '更新时间';
COMMENT ON COLUMN "public"."data_server_database"."priority" IS '数据库使用顺序(升序)';
COMMENT ON COLUMN "public"."data_server_database"."amount" IS '数据使用用户数量';
COMMENT ON COLUMN "public"."data_server_database"."status" IS '使用状态(1:正在使用;2:本库使用用户数已满)';
COMMENT ON TABLE "public"."data_server_database" IS '用户连接的数据库配置';
-- ----------------------------
-- Uniques structure for table data_server_database
-- ----------------------------
ALTER TABLE "public"."data_server_database" ADD CONSTRAINT "source_key_unique" UNIQUE ("data_source_key");
COMMENT ON CONSTRAINT "source_key_unique" ON "public"."data_server_database" IS '数据源名 唯一';
-- ----------------------------
-- Primary Key structure for table data_server_database
-- ----------------------------
ALTER TABLE "public"."data_server_database" ADD CONSTRAINT "data_server_source_pkey" PRIMARY KEY ("id");

image.gif

数据源表建表语句

CREATE TABLE "public"."data_server_source" (
  "id" int8 NOT NULL,
  "driver_class_name" varchar(128) COLLATE "pg_catalog"."default" NOT NULL,
  "url" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
  "user_name" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
  "password" varchar(128) COLLATE "pg_catalog"."default" NOT NULL,
  "create_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  "update_time" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  "priority" int4,
  "amount" int4 DEFAULT 0,
  "status" int2 DEFAULT 2
)
;
COMMENT ON COLUMN "public"."data_server_source"."id" IS '主键';
COMMENT ON COLUMN "public"."data_server_source"."driver_class_name" IS '数据库驱动';
COMMENT ON COLUMN "public"."data_server_source"."url" IS '数据库连接url';
COMMENT ON COLUMN "public"."data_server_source"."user_name" IS '数据库用户名';
COMMENT ON COLUMN "public"."data_server_source"."password" IS '数据库用户密码';
COMMENT ON COLUMN "public"."data_server_source"."create_time" IS '创建时间';
COMMENT ON COLUMN "public"."data_server_source"."update_time" IS '更新时间';
COMMENT ON COLUMN "public"."data_server_source"."priority" IS '数据库服务使用顺序(升序)';
COMMENT ON COLUMN "public"."data_server_source"."amount" IS '数据服务建库数量';
COMMENT ON COLUMN "public"."data_server_source"."status" IS '使用状态(1:正在使用;2:本服务建库数已满)';
COMMENT ON TABLE "public"."data_server_source" IS '数据库服务的数据源连接表';
-- ----------------------------
-- Records of data_server_source
-- ----------------------------
INSERT INTO "public"."data_server_source" VALUES (2, 'org.postgresql.Driver', 'jdbc:postgresql://localhost:5432/', 'hgl', 'hgl', '2021-11-01 14:53:45', '2021-11-01 14:53:47', 2, 0, 2);
INSERT INTO "public"."data_server_source" VALUES (1, 'org.postgresql.Driver', 'jdbc:postgresql://172.16.10.201:5432/', 'postgres', 'postgres', '2021-11-01 14:53:45', '2021-11-01 14:53:47', 1, 3, 1);
INSERT INTO "public"."data_server_source" VALUES (5, 'org.postgresql.Driver', 'jdbc:postgresql://172.16.10.6:5432/', 'hgl', 'hgl', '2021-11-01 14:54:12', '2021-11-01 14:54:14', 10, 0, 2);
INSERT INTO "public"."data_server_source" VALUES (10, 'org.postgresql.Driver', 'jdbc:postgresql://172.16.10.72:5432,172.16.10.73:5432/', 'postgres', 'pgpg', '2021-11-01 14:54:12', '2021-11-01 14:54:14', 10, 0, 2);
-- ----------------------------
-- Primary Key structure for table data_server_source
-- ----------------------------
ALTER TABLE "public"."data_server_source" ADD CONSTRAINT "data_server_source_pkey1" PRIMARY KEY ("id");

image.gif

用户表 省略,就是常规用户表,加上 数据库id外键即可

注册用户时,

调用DynamicDataSourceService类的getDatabaseId() 方法,将用户和数据库绑定。


user.setDatabaseId(dataSourceService.getDatabaseId());

image.gif

getDatabaseId() 讲解 根据配置的数据库最大用户数配置,方法内部判断当前数据库用户数是否大于配置用户,没有则返回当前数据库id,有则返回下一个数据库id

使用方法,调用接口时候,传入token ,动态数据库拦截器,自动获取用户id,切换对应数据源。


相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
187 2
|
23天前
|
SQL 前端开发 关系型数据库
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
49 9
|
27天前
|
缓存 监控 Java
|
27天前
|
缓存 监控 Java
|
2月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
123 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
2月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
60 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
28天前
|
存储 easyexcel Java
SpringBoot+EasyExcel轻松实现300万数据快速导出!
本文介绍了在项目开发中使用Apache POI进行数据导入导出的常见问题及解决方案。首先比较了HSSFWorkbook、XSSFWorkbook和SXSSFWorkbook三种传统POI版本的优缺点,然后根据数据量大小推荐了合适的使用场景。接着重点介绍了如何使用EasyExcel处理超百万数据的导入导出,包括分批查询、分批写入Excel、分批插入数据库等技术细节。通过测试,300万数据的导出用时约2分15秒,导入用时约91秒,展示了高效的数据处理能力。最后总结了公司现有做法的不足,并提出了改进方向。
|
2月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
375 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
2月前
|
easyexcel Java UED
SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载
在SpringBoot环境中,为了优化大量数据的Excel导出体验,可采用异步方式处理。具体做法是将数据拆分后利用`CompletableFuture`与`ThreadPoolTaskExecutor`并行导出,并使用EasyExcel生成多个Excel文件,最终将其压缩成ZIP文件供下载。此方案提升了导出效率,改善了用户体验。代码示例展示了如何实现这一过程,包括多线程处理、模板导出及资源清理等关键步骤。
|
3月前
|
JavaScript Java 关系型数据库
毕设项目&课程设计&毕设项目:基于springboot+vue实现的在线考试系统(含教程&源码&数据库数据)
本文介绍了一个基于Spring Boot和Vue.js实现的在线考试系统。随着在线教育的发展,在线考试系统的重要性日益凸显。该系统不仅能提高教学效率,减轻教师负担,还为学生提供了灵活便捷的考试方式。技术栈包括Spring Boot、Vue.js、Element-UI等,支持多种角色登录,具备考试管理、题库管理、成绩查询等功能。系统采用前后端分离架构,具备高性能和扩展性,未来可进一步优化并引入AI技术提升智能化水平。
毕设项目&课程设计&毕设项目:基于springboot+vue实现的在线考试系统(含教程&源码&数据库数据)