SpringBoot+MyBatis-Plus+jsqlparser实现多租户功能

前言

多租户技术(multi-tenancy technology)是一种软件架构技术,它允许在单个系统实例上为多个用户或组织提供服务,同时确保这些用户之间数据的隔离性。在多租户架构中,每个租户(可以是个人用户、企业、组织等)共享相同的应用程序实例、硬件资源和基础设施,但数据和配置是相互隔离的,每个租户只能访问自己的数据和配置,互不干扰。

功能描述

  • 基于字段tenant_id数据隔离租户
  • 排除特殊表
  • 排除特殊查询
  • 异步支持

代码实现

依赖引入

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.2</version>
</dependency>

<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>3.1</version>
</dependency>

yml配置

# mybatis-plus配置
mybatis-plus:
  # 启动检查MyBatis配置文件
  check-config-location: false
  # MyBatis配置文件位置
  config-location:
  # MyBaits别名包扫描路径
  type-aliases-package: com.qiangesoft.tenantid.entity
  # Mapper所对应的XML文件位置 默认【classpath*:/mapper/**/*.xml】
  mapper-locations: classpath*:/mapper/*Mapper.xml
  # TypeHandler扫描路径
  type-handlers-package:
  configuration:
    # 日志打印
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 是否开启自动驼峰命名规则
    map-underscore-to-camel-case: true
    # 开启Mybatis二级缓存,默认为true
    cache-enabled: true
  global-config:
    # 控制台mybatis-plus的logo
    banner: true
    db-config:
      # 全局默认主键类型
      id-type: auto
      # 逻辑删除配置
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

配置类

package com.qiangesoft.tenantid.config;

import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * mybatis-plus配置
 *
 * @author qiangesoft
 * @date 2024-04-11
 */
@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();

        List<ISqlParser> sqlParserList = new ArrayList<>();
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(new TenantHandler() {
            @Override
            public Expression getTenantId(boolean select) {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                String tenantId = request.getHeader("tenantId");
                return new StringValue(tenantId);
            }

            @Override
            public String getTenantIdColumn() {
                return "tenant_id";
            }

            /**
             * 整张表租户过滤排除
             * @param tableName
             * @return
             */
            @Override
            public boolean doTableFilter(String tableName) {
                List<String> ignoreTables = Arrays.asList("sys_demo");
                return ignoreTables.stream().anyMatch(e -> e.equalsIgnoreCase(tableName));
            }
        });
        sqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(sqlParserList);
        return paginationInterceptor;
    }

}

排除特殊查询

  • @SqlParser(filter = true)只能作用于mapper层
package com.qiangesoft.tenantid.mapper;

import com.baomidou.mybatisplus.annotation.SqlParser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qiangesoft.tenantid.entity.SysDept;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * <p>
 * 部门信息 Mapper 接口
 * </p>
 *
 * @author qiangesoft
 * @since 2024-04-11
 */
public interface SysDeptMapper extends BaseMapper<SysDept> {

    /**
     * 不进行租户过滤
     *
     * @param parentId
     * @return
     */
    @SqlParser(filter = true)
    List<SysDept> listByParam(@Param("parentId") Long parentId);

}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qiangesoft.tenantid.mapper.SysDeptMapper">

    <select id="listByParam" resultType="sysDept">
        select *
        from sys_dept
        where parent_id = #{parentId}
    </select>

</mapper>

package com.qiangesoft.tenantid.controller;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qiangesoft.tenantid.entity.SysDept;
import com.qiangesoft.tenantid.entity.SysUser;
import com.qiangesoft.tenantid.service.ISysDeptService;
import com.qiangesoft.tenantid.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * <p>
 * 用户信息 前端控制器
 * </p>
 *
 * @author qiangesoft
 * @since 2024-04-11
 */
@Slf4j
@RestController
@RequestMapping("/sys-user")
public class SysUserController {

    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISysDeptService sysDeptService;

    @GetMapping("/list")
    public List<SysUser> list() {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(SysUser::getCreateTime);
        List<SysUser> sysUserList = sysUserService.list(queryWrapper);
        log.info(JSON.toJSONString(sysUserList));

        List<SysDept> sysDeptList = sysDeptService.listByParam(1L);
        log.info(JSON.toJSONString(sysDeptList));

        return sysUserList;
    }

}
package com.qiangesoft.tenantid.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.qiangesoft.tenantid.entity.SysDept;

import java.util.List;

/**
 * <p>
 * 部门信息 服务类
 * </p>
 *
 * @author qiangesoft
 * @since 2024-04-11
 */
public interface ISysDeptService extends IService<SysDept> {

    /**
     * 查询部门
     *
     * @param parentId
     * @return
     */
    List<SysDept> listByParam(Long parentId);

}

在这里插入图片描述

在这里插入图片描述

异步支持

package com.qiangesoft.tenantid.controller;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qiangesoft.tenantid.entity.SysDept;
import com.qiangesoft.tenantid.entity.SysUser;
import com.qiangesoft.tenantid.service.ISysDeptService;
import com.qiangesoft.tenantid.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * <p>
 * 用户信息 前端控制器
 * </p>
 *
 * @author qiangesoft
 * @since 2024-04-11
 */
@Slf4j
@RestController
@RequestMapping("/sys-user")
public class SysUserController {

    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISysDeptService sysDeptService;

    @GetMapping("/sync")
    public List<SysDept> sync() throws ExecutionException, InterruptedException {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(SysUser::getCreateTime);
        List<SysUser> sysUserList = sysUserService.list(queryWrapper);
        log.info(JSON.toJSONString(sysUserList));

        // 异步查询部门
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Callable getUser = () -> {
            RequestContextHolder.setRequestAttributes(sra, true);
            List<SysDept> sysDeptList = sysDeptService.list();
            log.info(JSON.toJSONString(sysDeptList));
            return sysUserList;
        };
        FutureTask<List<SysDept>> future = new FutureTask<>(getUser);
        new Thread(future).start();
        return future.get();
    }

}

在这里插入图片描述

在这里插入图片描述

请求调用

  • 请求头添加tenantId
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/576474.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python中开发页面的两种方法:Qt Designer(PyQt图形化界面拖拽开发App界面)以及Django(开发Web应用框架)

一、开发独立的窗口&#xff0c;App的那种&#xff0c;可使用tkinter或者PyQt 使用PyQt时&#xff0c;里面有个工具Qt Designer&#xff0c;是一个可视化的界面设计工具&#xff0c;可以通过拖拽等方式来设计界面。下面就是Qt Designer的操作界面&#xff1a; 参考链接如下&am…

代码随想录-算法训练营day24【回溯01:理论基础、组合】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第七章 回溯算法part01 今日内容&#xff1a;● 理论基础 ● 77. 组合 详细布置 理论基础 其实在讲解二叉树的时候&#xff0c;就给大家介绍过回溯&#xff0c;这次正式开启回溯算法&#xff0c;大家可以先看视频&a…

docker入门级命令

基本概念 docker的连个基本概念&#xff1a;镜像、容器。 docker镜像可以理解为是存储docker安装包的地方&#xff0c;比如&#xff1a;mcr.microsoft.com/mssql/server:2017-latest是sqlserver的docker镜像。 可以通过docker pull命令拉取远程镜像到本地。比如&#xff1a;…

【论文浅尝】Phi-3-mini:A Highly Capable Language Model Locally on Your Phone

Phi-3-mini phi-3-mini&#xff0c;一个3.8亿个参数的语言模型&#xff0c;训练了3.3万亿个token&#xff0c;其总体性能&#xff0c;通过学术基准和内部测试进行衡量&#xff0c;可以与Mixtral 8x7B和GPT-3.5等模型相媲美(在MMLU上达到69%&#xff0c;在MT-bench上达到8.38)&…

什么是云手机?云手机有什么用?

过去&#xff0c;我们手中的手机是我们生活、工作、娱乐的得力助手&#xff0c;但随着时代的变迁和技术的发展&#xff0c;我们需要的不仅仅是一部手机&#xff0c;而是一个更强大、更灵活的工具。在这个时候&#xff0c;云手机横空出世&#xff0c;成为了我们手机使用的新选择…

[GXYCTF 2019]BabyUpload

过滤 <? 且后缀不能有 php 上传1.jpg文件&#xff0c;内容为&#xff1a; <script languagephp>eval($_POST[cmd]);</script> 但文件后缀为.jpg&#xff0c;蚁剑不能连接。那怎么办呢&#xff1f; .htaccess文件&#xff1a;解析.jpg文件中的php代码 &#xf…

Druid高性能数据库连接池?SpringBoot整合MyBatis整合SpringMVC整合Druid

文章目录 Druid高性能数据库连接池&#xff1f;SpringBoot整合MyBatis整合SpringMVC整合Druid异常记录spring-boot-starter-parent作用Druid介绍什么是数据库连接池&#xff1f;为什么选择Druid数据库连接池整合SpringBoot,MyBatis,SpringMVC,Druid到Maven项目的真个流程pom文件…

Redis入门到实战教程(基础篇)笔记

教学来源&#xff1a; Redis课程介绍导学_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1cr4y1671t?p1一、Redis 入门 1.认识NoSQL 2.Redis在虚拟机中的安装和开机自启 Redis在虚拟机中安装和配置开机自启-CSDN博客https://blog.csdn.net/qq_69183322/article/deta…

MT8788智能模块简介_MTK联发科安卓核心板方案厂商

MT8788安卓核心板是一款具备超高性能和低功耗的4G全网通安卓智能模块。该模块采用联发科AIOT芯片平台&#xff0c;供货周期长。 MT8788核心板搭载了12nm制程的四个Cortex-A73处理器核心和四个Cortex-A53处理器核心&#xff0c;最高主频可达2.0GHz。板载内存容量可选为4GB64GB(也…

docker 基本命令

目录 一、docker 镜像操作命令 1.1.查询软件镜像 1.2.docker pull&#xff1a;下载镜像 1.3.docker push&#xff1a;上传镜像 1.4.docker images&#xff1a;查看本地镜像 1.5.docker inspect &#xff1a;获取镜像详细信息 1.6.docker tag&#xff1a;添加镜像标签 …

4.28|重量级嘉宾携卓翼飞思RflySim平台亮相国际盛会,内容抢先看!

一. 大会背景 2024国际无人机应用及防控大会暨无人机产业博览会即将拉开帷幕&#xff0c;一场高规格、高水平的无人机产业应用国际盛会将再次点亮科技界的星空。 该大会由中国无人机产业创新联盟联合各方有影响力的单位&#xff0c;于4月27-29日在北京举办。组委会致力于将会…

【Python系列】受保护属性

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

RAG原理及本地化实践

基于LLM的应用在问题回答、信息获取上发挥出了巨大作用。这些通用大模型训练的数据主要来源于互联网上的会话或者个别机构提供的数据&#xff0c;虽然能够提供类似人的交互对答&#xff0c;但是在针对某个特定领域的时候就显得不足。通用大模型在应用中主要有以下问题&#xff…

【DINO】环境配置

1. DINO简介 作为一款基于Transformer性能强劲的计算机视觉算法&#xff0c;一经发布即受追捧&#xff0c;本文记录下在DINO官方代码在集群上的环境配置及训练自己的数据集过程。 DINO原文&#xff1a;https://arxiv.org/abs/2203.03605 DINO源代码&#xff1a;https://github.…

ssm084基于ssm的大型商场会员管理系统+jsp

大型商场会员管理系统的设计与实现 摘 要 进入信息时代以来&#xff0c;很多数据都需要配套软件协助处理&#xff0c;这样可以解决传统方式带来的管理困扰。比如耗时长&#xff0c;成本高&#xff0c;维护数据困难&#xff0c;数据易丢失等缺点。本次使用数据库工具MySQL和编…

【C语言必刷题】7. 百钱百鸡

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

《汇编语言》- 读书笔记 - 综合研究

《汇编语言》- 读书笔记 - 综合研究 研究试验 1 搭建一个精简的 C 语言开发环境1. 下载2. 配置3. 编译4. 连接 研究试验 2 使用寄存器1. 编一个程序 ur1.c &#xff08; tcc 用法&#xff09;tcc 编译连接多个源文件tlink 手动连接 2.用 Debug 加载 ur1.exe&#xff0c;用u命令…

数据转换 | Matlab基于RP递归图一维数据转二维图像方法

目录 效果分析基本介绍程序设计参考资料获取方式 效果分析 基本介绍 Matlab基于RP递归图一维数据转二维图像方法 基于RP&#xff08;Recurrence Plot&#xff09;递归图的方法可以将一维数据转换为二维图像&#xff0c;以可视化数据的动态特征。RP递归图是一种表示时间序列相…

android 去除桌面谷歌搜索框

注&#xff1a; 本文只是博主学习记录分享&#xff0c;仅供参考。如有错误请指出来&#xff0c;谢谢&#xff01; 一、问题描述 去除 android 系统桌面谷歌搜索栏&#xff0c;前后对比如下图&#xff1a; 系统版本&#xff1a;android12 平台&#xff1a;rk3568 二、…

【小浩算法cpp题解】判断环形链表

目录 前言我的思路思路一 &#xff08;哈希表记录链表的访问&#xff09;&#xff1a;思路二 &#xff08;双指针&#xff0c;快指针在前&#xff0c;慢指针在后&#xff09;&#xff1a; 我的代码运行结果 前言 前几天我写的代码&#xff0c;都是把所有的内容写在main函数里&…
最新文章