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二级缓存,默认为truecache-enabled: trueglobal-config:# 控制台mybatis-plus的logobanner: truedb-config:# 全局默认主键类型id-type: auto# 逻辑删除配置logic-delete-field: deletedlogic-delete-value: 1logic-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 {@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();List<ISqlParser> sqlParserList = new ArrayList<>();TenantSqlParser tenantSqlParser = new TenantSqlParser();tenantSqlParser.setTenantHandler(new TenantHandler() {@Overridepublic Expression getTenantId(boolean select) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();String tenantId = request.getHeader("tenantId");return new StringValue(tenantId);}@Overridepublic String getTenantIdColumn() {return "tenant_id";}/*** 整张表租户过滤排除* @param tableName* @return*/@Overridepublic 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_deptwhere 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 {@Autowiredprivate ISysUserService sysUserService;@Autowiredprivate 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 {@Autowiredprivate ISysUserService sysUserService;@Autowiredprivate 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.mzph.cn/news/829234.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

预见预判|AIRIOT智慧交通管理解决方案

随着机动车保有量的逐步增加&#xff0c;城市交通压力日益增大。同时&#xff0c;新能源车辆的快速发展虽然带来了环保效益&#xff0c;但也因不限号政策而进一步加剧了道路拥堵问题。此外&#xff0c;各类赛事和重大活动的交通管制措施也时常导致交通状况复杂多变。面对这些挑…

便携式应急指挥箱规格参数

概况: 微缩型的无线视频音频传输的机动挥所。体积小、重量轻、公配电方便、携带便携、功能齐全。可进行单兵作战&#xff0c;通过此无线音频视频传输的指挥箱能完成现场图像、语音、数据的采集等功能&#xff0c;可以通过5G/4G/WIFI等多种无线网络完成传输的需求&#xff0c;或…

Linux基础——Linux开发工具(gcc/g++,gdb)

前言&#xff1a;在上一篇我们简单介绍了yum&#xff0c;vim的一些常用的指令和模式&#xff0c;现在让我们来进一步了解其他的Linux环境基础开发工具gcc/g&#xff0c;gdb。 如果对前面yum和vim有什么不懂的建议回顾去回顾上期知识&#xff01;&#xff01;&#xff01; Linu…

html表格导出为word文档,导出的部分表格内无法填写文字

导出技术实现&#xff1a;fileSaver.jshtml-docx-js 1.npm安装 npm install --save html-docx-js npm install --save file-saver 2.页面引入 import htmlDocx from html-docx-js/dist/html-docx; import saveAs from file-saver;components: {htmlDocx,saverFile, }, 3.页…

神经网络的激活函数

目录 神经网络 激活函数 sigmoid 激活函数 tanh 激活函数 backward方法 relu 激活函数 softmax 激活函数 神经网络 人工神经网络&#xff08; Artificial Neural Network&#xff0c; 简写为ANN&#xff09;也简称为神经网络&#xff08;NN&#xff09;&#xff0c…

Netty 应用与原理

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 Java IO 模型 本篇示例代码仓库&#xff1a;learn-netty 基础概念 在 I/O 操作中有这么两组概念&#xff0c;其中同步/异步 要和线程中的同步线程/异步线程要区分开&#xff0c;这里指的是同步IO / 异步IO…

ssm智能停车场管理系统

视频演示效果: SSMvue智能停车场 摘 要 本论文主要论述了如何使用JAVA语言开发一个智能停车场管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述智能停车…

新兴游戏引擎Godot vs. 主流游戏引擎Unity和虚幻引擎,以及版本控制工具Perforce Helix Core如何与其高效集成

游戏行业出现一个新生事物——Godot&#xff0c;一个免费且开源的2D和3D游戏引擎。曾经由Unity和虚幻引擎&#xff08;Unreal Engine&#xff09;等巨头主导的领域如今迎来了竞争对手。随着最近“独特”定价模式的变化&#xff0c;越来越多的独立开发者和小型开发团队倾向于选择…

Vitis HLS 学习笔记--Syn Report解读(1)

目录 1. 介绍 2. 示例一 2.1 HLS 代码 2.2 Report 解读 2.2.1 General Information 2.2.2 Timing Estimate 2.2.3 Performance & Resource Estimates 2.2.4 HW interfaces 2.2.4.1 硬件接口报告 2.2.4.2 导出至 Vivado 中的 IP 2.2.4.3 Port-Level Protocols 端…

图形化编程要怎么做

0. 简介 Scratch其实应该算得上最早做图形化编程的工程了。Scratch 是麻省理工学院的“终身幼儿园团队”在 2007 年 [5]发布的一种图形化编程工具&#xff0c;主要面对全球青少年开放&#xff0c;是图形化编程工具当中最广为人知的一种&#xff0c;所有人都可以在软件中创作自…

排序算法-计数排序

一、计数排序 这种排序算法 是利用数组下标来确定元素的正确位置的。 如果数组中有20个随机整数&#xff0c;取值范围为0~10&#xff0c;要求用最快的速度把这20个整数从小到大进行排序。 很大的情况下&#xff0c;它的性能甚至快过那些时间复杂度为O(nlogn&#xff09;的排序。…

【React】Sigma.js框架网络图-入门篇(2)

通过《【React】Sigma.js框架网络图-入门篇》有了基本认识 由于上一篇直接给出了基本代码示例&#xff0c;可能看着比较复杂也不知道是啥意思&#xff1b; 今天从理论入手重新认识下&#xff01; 一、基本认识 首先&#xff0c;我们先了解下基础术语&#xff1a; 图(Graph)&…

如何实现直播声卡反向给手机充电功能呢?

在数字化时代的浪潮中&#xff0c;声卡作为多媒体系统的核心组件&#xff0c;扮演着声波与数字信号相互转换的关键角色。它不仅能够将来自各类音源的原始声音信号转换为数字信号&#xff0c;进而输出到各类声响设备&#xff0c;更能够通过音乐设备数字接口(MIDI)发出合成乐器的…

Eudic欧路词典for Mac:专业英语学习工具

Eudic欧路词典for Mac&#xff0c;作为专为Mac用户设计的英语学习工具&#xff0c;凭借其简捷高效的特点&#xff0c;成为众多英语学习者不可或缺的助手。 Eudic欧路词典for Mac v4.6.4激活版下载 这款词典整合了多个权威词典资源&#xff0c;如牛津、柯林斯、朗文等&#xff0…

​「Python大数据」词频数据渲染词云图导出HTML

前言 本文主要介绍通过python实现数据聚类、脚本开发、办公自动化。词频数据渲染词云图导出HTML。 一、业务逻辑 读取voc数据采集的数据批处理,使用jieba进行分词,去除停用词词频数据渲染词云图将可视化结果保存到HTML文件中二、具体产出 三、执行脚本 python wordCloud.p…

mysql中日期函数now()和sysdate()的区别

说明&#xff1a; 在mysql数据库中&#xff0c;now()、sysdate() 都是获取系统当前日期时间 区别&#xff1a; 示例脚本&#xff1a; select sleep(2),now(),sysdate() from dual; 输出&#xff1a; 小结&#xff1a; 1、 now()和sysdate()都可以获取系统当前日期时间 2、区别…

将游戏界面与注册/登录界面连接到一起

一、 导包 在注册页面中导入一个import subprocess包 二、 使用代码将其连接到一起 在循环中加入下面这一行代码&#xff0c;用来实现效果 subprocess.run(["python", "game代码.py"]

Faust勒索病毒:了解变种faust,以及如何保护您的数据

导言&#xff1a; 近年来&#xff0c;网络安全问题日益严峻&#xff0c;其中勒索病毒成为了一种日益猖獗的威胁。在众多勒索病毒中&#xff0c;.faust勒索病毒以其高度的隐秘性和破坏性引起了广泛关注。本文91数据恢复将深入剖析.faust勒索病毒的威胁特点&#xff0c;并提出相…

实现SpringMVC底层机制(一)

文章目录 1.环境配置1.创建maven项目2.创建文件目录3.导入jar包 2.开发核心控制器文件目录1.流程图2.编写核心控制器SunDispatcherServlet.java3.类路径下编写spring配置文件sunspringmvc.xml4.配置中央控制器web.xml5.配置tomcat&#xff0c;完成测试1.配置发布方式2.配置热加…

【stomp 实战】Spring websocket使用详解和基本原理

spring框架对websocket有很好的支持&#xff0c;stomp协议作为websocket的子协议&#xff0c;Spring也做了很多封装&#xff0c;让我们在开发中易于使用。 学习使用Spring的Websocket模块&#xff0c;当然最好的办法就是看官网说明了。本篇文章对官网做一些简述和个人的理解。 …