跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaSaaSjava

SpringBoot多租户实战:动态数据源与租户隔离

基于SpringBoot的SaaS多租户实践,采用共享数据库独立Schema模式,利用ThreadLocal和MyBatis拦截器实现透明数据隔离,并通过AbstractRoutingDataSource进行动态数据源切换。涉及租户上下文传递、SQL改写和配置示例。

信号故障发布于 2026/6/28更新于 2026/9/1137 浏览

多租户架构设计

在SaaS平台里,多个租户共用一套应用,数据隔离是核心。我们选了共享数据库、独立Schema的方案——在资源利用率和隔离性之间找了个平衡。

租户上下文传递

用ThreadLocal保存当前请求的租户ID,结合AOP从请求头里拿出来。

public class TenantContext {
    private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();

    public static void setTenantId(String tenantId) {
        currentTenant.set(tenantId);
    }

    public static String getTenantId() {
        return currentTenant.get();
    }

    public static void clear() {
        currentTenant.remove();
    }
}

@Aspect
@Component
public class TenantAspect {
    @Before("execution(* com.jeespring..service..*.*(..))")
    public void beforeService() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String tenantId = request.getHeader("X-Tenant-ID");
        TenantContext.setTenantId(tenantId);
    }

    @After("execution(* com.jeespring..service..*.*(..))")
       {
        TenantContext.clear();
    }
}
public
void
afterService
()

这样每次业务操作前都绑定了租户ID,完事清理掉。注意,如果线程池复用,记得用类似TransmittableThreadLocal避免泄露。

SQL自动改写

MyBatis拦截器在SQL执行前,根据租户ID加上过滤条件。

@Intercepts({
    @Signature(type= StatementHandler.class, method="prepare", args={Connection.class, Integer.class})
})
public class TenantInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        String tenantId = TenantContext.getTenantId();
        if (tenantId == null) {
            return invocation.proceed();
        }
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql();
        String newSql = addTenantCondition(sql, tenantId);
        resetSql(handler, boundSql, newSql);
        return invocation.proceed();
    }

    private String addTenantCondition(String sql, String tenantId) {
        // 不建议直接字符串拼接,用SQL解析库防止注入
        return SqlParser.addTenantCondition(sql, tenantId);
    }
}

那个'提示'部分改写为注释或正文里说,不单独用块引用提示。

动态数据源配置

当不同租户数据库实例不同时,需要动态切换数据源。Spring的AbstractRoutingDataSource是个现成的选择。

配置文件

spring:
  datasource:
    master:
      url: jdbc:mysql://localhost:3306/master_db
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    tenant1:
      url: jdbc:mysql://localhost:3306/tenant1_db
      username: tenant1
      password: tenant1pass
      driver-class-name: com.mysql.cj.jdbc.Driver
    tenant2:
      url: jdbc:mysql://localhost:3306/tenant2_db
      username: tenant2
      password: tenant2pass
      driver-class-name: com.mysql.cj.jdbc.Driver

路由实现

public class TenantDataSourceRouter extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.getTenantId();
    }
}

@Configuration
public class DataSourceConfig {

目录

  1. 多租户架构设计
  2. 租户上下文传递
  3. SQL自动改写
  4. 动态数据源配置
  5. 配置文件
  6. 路由实现

更多推荐文章

查看全部
  • 微调效果不佳?Llama-Factory 内置诊断工具定位问题
  • CodeGeeX:一款可替代 Github Copilot 的国产编程助手
  • Docker 安装部署与核心使用指南(Linux 通用版)
  • 数论之中国剩余定理:从孙子算经到算法竞赛实现
  • 从执行到战略:AI 大模型与 S2B2C 重构运营价值体系
  • Telegram 中文搜索机器人@letstgbot 技术原理与开发实践
  • 3661. 可以被机器人摧毁的最大墙壁数目:离散化与动态规划实战
  • Vite7+Vue3+DeepSeek-R1 流式 AI 聊天系统搭建
  • LLaMA-Factory 大模型高效微调实战指南
  • MS-S1 MAX 与 AI MAX 395 在 Ubuntu 24 使用 Vulkan 版 llama.cpp 运行 gpt-oss 120b
  • 命令行工具 MCPHost:实现大模型与外部工具的 MCP 交互
  • C++ 继承中同名成员的隐藏规则解析
  • GitHub Copilot 兼容 OpenAI/Anthropic API 的代理工具 copilot-api
  • MySQL 事务:从基础概念到隔离性理论与实践
  • 技术冷笑话:JS 与 Java 的跨语言隔阂
  • FPGA 光通信开发:Aurora 64B/66B 使用指南
  • Vivado 2035 注册错误解决方案与许可证配置指南
  • 大模型面试常见问题全解析:RAG、SFT、RLHF 与架构详解
  • 企业级 Web 农产品直卖平台管理系统:SpringBoot+Vue+MyBatis 架构
  • C++ 类与对象内在机制:运算符重载、赋值重载与取址重载

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online