Mybatis-Plus同时实现分表和表内多租户模式

在之前经理的某家公司中,经历了一个saas服务的某些功能的数据量不断变大的过程,因为各种功能和性能的原因想到的方法就是直接按saas租户做分库和按租户对某些数据量大的表做分表。但是在我离职之前这两种方式都未能实现。不过,最近刚好看到Mybatis-Plus的多租户的拦截器功能,想到可以用来做第二种方案的问题的解决方法,因此来尝试一番。 使用最新版Mybatis-Plus

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

需要配合使用DynamicTableNameInnerInterceptor和TenantLineInnerInterceptor

DynamicTableNameInnerInterceptor

利用DynamicTableNameInnerInterceptor主要是用来对某些数据量大的表做分表查询的,这个拦截器可以在执行sql语句的时候动态的修改查询的表名。使用方法如下

   //这里我将租户id写死了。真实的实现中应当从当前登录的数据中获取
   private static final String tenant_id = "zhao";
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor =new MybatisPlusInterceptor();
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor= new DynamicTableNameInnerInterceptor();
        TableNameHandler tableNameHandler = new TableNameHandler() {
            @Override
            public String dynamicTableName(String sql, String tableName) {
                //tenantProperty中动态标识的是哪些表是分表的表,就在哪些分表的表添加租户的表后缀
                //可以将tenantProperty的配置修改为数据库配置也可以,改动更灵活
                if (tenantProperty.getShardingTables().contains(tableName)){
                    return tableName+"_"+tenant_id;
                }
                return tableName;
            }
        };
        dynamicTableNameInnerInterceptor.setTableNameHandler(tableNameHandler);
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);

        return interceptor;
    }

TenantLineInnerInterceptor

Mybatis自带的无自定义的租户的拦截器会在所有的sql后面加上对应的租户条件,但是我们可以自定义对应的处理租户信息相关的Handler.

public class MultiTenantLineHandler implements TenantLineHandler {

    private TenantProperty tenantProperty;

    public MultiTenantLineHandler(TenantProperty tenantProperty){
        this.tenantProperty =tenantProperty;
    }
    @Override
    public Expression getTenantId() {
        //此处直接使用给定租户,实际实现从登录信息中取出
        return new StringValue("zhao");
    }

    @Override
    public String getTenantIdColumn() {
        //租户列名
        return tenantProperty.getTenantColumn();
    }

    //需要忽略的表的配置
    @Override
    public boolean ignoreTable(String tableName) {
        List<String> ignoreTables = tenantProperty.getIgnoreTables();
        if (ignoreTables.contains(tableName)){
            return true;
        }
        return false;
    }
//    不处理的非租户列的insert
//    @Override
//    public boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
//        return TenantLineHandler.super.ignoreInsert(columns, tenantIdColumn);
//    }
}

自定义配置类TenantProperty和拦截器整合

配置类

@Data
@Configuration
@ConfigurationProperties(prefix = "tenant")
public class TenantProperty {

    private Boolean enable =true;

    private String tenantColumn="tenant_id";

    private List<String> ignoreTables;

    private List<String> shardingTables;



}

配置信息

tenant:
  enabletrue
  ignoreTables:
    - sharding
  shardingTables:
    - sharding

整合拦截器

@Configuration
@MapperScan("com.zhao.sbsc17.dao")
public class TableTenantConfig {
//    @Bean
//    public MybatisPlusInterceptor mybatisPlusInterceptor(){
//        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//        return interceptor;
//    }
    private static final String tenant_id = "zhao";
    @Autowired
    TenantProperty tenantProperty;
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor =new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new MultiTenantLineHandler(tenantProperty)));
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor= new DynamicTableNameInnerInterceptor();
        TableNameHandler tableNameHandler = new TableNameHandler() {
            @Override
            public String dynamicTableName(String sql, String tableName) {
                if (tenantProperty.getShardingTables().contains(tableName)){
                    return tableName+"_"+tenant_id;
                }
                return tableName;
            }
        };
        dynamicTableNameInnerInterceptor.setTableNameHandler(tableNameHandler);
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);

        return interceptor;
    }
}

需要说明的是两个拦截器添加的顺序如果不同会有不同的效果,那么也就需要做对应的处理。当前demo我是达到了刚好我要使用的效果。

效果

新建表tenant和sharding_zhao

查询

@RestController
@RequestMapping("tenant")
public class TenantController {

    @Autowired
    ShardingMapper shardingMapper;
    @Autowired
    TenantMapper tenantMapper;

    @GetMapping("tenant")
    public Tenant master(){
        return tenantMapper.selectById(1L);
    }

    @GetMapping("sharding")
    public Sharding sharding(){
        return shardingMapper.selectById(1L);
    }

}

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@213354a5] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@70191485] will not be managed by Spring
==>  Preparing: SELECT id, goods_id, goods_name, num, version, tenant_id FROM tenant WHERE id = ? AND tenant_id = 'zhao'
==> Parameters: 1(Long)
<==    Columns: id, goods_id, goods_name, num, version, tenant_id
<==        Row: 1, 1, 测试master, 1, 1, zhao
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@213354a5]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7aebce73] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@70191485] will not be managed by Spring
==>  Preparing: SELECT id, goods_id, goods_name, num, version FROM sharding_zhao WHERE id = ?
==> Parameters: 1(Long)
<==    Columns: id, goods_id, goods_name, num, version
<==        Row: 1, 1, 1, 1, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7aebce73]

新增

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@14a9086f] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@48087898] will not be managed by Spring
==>  Preparing: INSERT INTO sharding_zhao (id, goods_name, num, version) VALUES (?, ?, ?, ?)
==> Parameters: 2(Long), 测试(String), 1(Long), 1(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@14a9086f]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@53a2e535] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@2054630b] will not be managed by Spring
==>  Preparing: INSERT INTO tenant (id, goods_name, num, version, tenant_id) VALUES (?, ?, ?, ?, 'zhao')
==> Parameters: 2(Long), 测试(String), 1(Long), 1(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@53a2e535]

本文由 mdnice 多平台发布

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

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

相关文章

遇到运维故障,有没有排查和解决故障的正确流程?

稳定是偶然&#xff0c;异常才是常态&#xff0c;用来标注IT运维工作再适合不过。 因为对于IT运维来说&#xff0c;工作最常遇到的就是不稳定性带来的各种故障&#xff0c;经常围绕发现故障、响应故障、定位故障、恢复故障这四大步。 故障处理是最心跳的事情&#xff0c;没有…

XETUX软件 dynamiccontent.properties.xhtml RCE漏洞复现

0x01 产品简介 XETUX 是一个全面的解决方案,包括一套安全、强大和可监控的软件程序,专为自动控制餐厅和零售而设计和开发。 0x02 漏洞概述 XETUX 存在代码执行漏洞,攻击者可通过 dynamiccontent.properties.xhtml 执行任意代码获取服务器权限。 0x03 复现环境 FOFA:ti…

Linux kernel 协议栈与高速网络

都知道 linux kernel 无力处理高速网络&#xff0c;但各种 offloading 方案也不全对&#xff0c;所以到底为什么 linux kernel 对高速网络无力。 中断&#xff0c;锁&#xff0c;上下文切换&#xff0c;内存拷贝这些经理都够得着的视野就别烦世人皆俯视了。本文给出另一个视角…

SQL进阶理论篇(四):索引的结构原理(B树与B+树)

文章目录 简介如何评价索引的数据结构设计好坏二叉树的局限性什么是B树什么是B树总结参考文献 简介 我们在上一节中说过&#xff0c;索引其实是一种数据结构&#xff0c;那它到底是一种什么样的数据结构呢&#xff1f;本节将简单介绍一下几个问题&#xff1a; 什么样的数据结…

<JavaEE> 网络编程 -- 网络通信基础(协议和协议分层、数据封装和分用)

目录 一、IP地址 1&#xff09;IP地址的概念 2&#xff09;IP地址的格式 二、端口号 1&#xff09;端口号的概念 2&#xff09;端口号的格式 3&#xff09;什么是知名端口号&#xff1f; 三、协议 1&#xff09;协议的概念 2&#xff09;协议的作用 3&#xff09;TC…

[笔记] iperf3.1.3源码下载与交叉编译

由于需要测试一款40G网卡&#xff0c;下载了 iperf3.1.3 用于性能测试。 iperf3.1.3 源码下载 可以在 iperf 官网 下载源代码&#xff1a; 交叉编译 需要运行在 aarch64 linux 环境下&#xff0c;所以需要交叉编译。 进入iperf3 目录下&#xff0c;运行 ./configure 脚本…

ssm基于HTML5的OA办公系统论文

基于HTML5的OA办公系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;作为一个一般的企业都开始注重与自己的信息展示平台&#xff0c;实现基于HTML5的OA办公系统在技术上已成熟。本文介绍了基于HTML5的OA办公系统的开发全过程。通过分析企业对于博客网站的需…

打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion 模特假人换服装、换背景、换真人

给固定人物换背景或者换服装,需要用到一个Stable Diffusion扩展插件,就是sd-webui-segment-anything。 sd-webui-segment-anything 不仅可以做到抠图的效果,也能实现之多蒙版的效果。 什么是蒙版 图片蒙版是一种用于调节图像修改程度以及进行局部调整的工具。它通常分为四种…

安全算法(二):共享密钥加密、公开密钥加密、混合加密和迪菲-赫尔曼密钥交换

安全算法&#xff08;二&#xff09;&#xff1a;共享密钥加密、公开密钥加密、混合加密和迪菲-赫尔曼密钥交换 本章介绍了共享密钥加密、公开密钥加密&#xff0c;和两种加密方法混合使用的混合加密方法&#xff1b;最后介绍了迪菲-赫尔曼密钥交换。 加密数据的方法可以分为…

16--常用类和基础API--06

1、包装类 1.1 包装类概述 Java提供了两个类型系统&#xff0c;基本类型与引用类型&#xff0c;使用基本类型在于效率&#xff0c;然而很多情况&#xff0c;会创建对象使用&#xff0c;因为对象可以做更多的功能&#xff0c;如果想要我们的基本类型像对象一样操作&#xff0c…

Gitlab基础篇: Gitlab docker 安装部署、Gitlab 设置账号密码

文章目录 1、环境准备2、配置1)、初始化2)、修改gitlab配置文件3)、修改docker配置的gitlab默认端口 gitlab进阶配置gitlab 设置账号密码 1、环境准备 安装docker gitlab前确保docker环境&#xff0c;如果没有搭建docker请查阅“Linux docker 安装文档” docker 下载 gitlab容…

STM32 PVD掉电检测功能的使用方法

STM32 PVD掉电检测功能的使用方法 前言 在实际应用场景中&#xff0c;可能会出现设备电源电压异常下降或掉电的情况&#xff0c;因此&#xff0c;有时候需要检测设备是否掉电&#xff0c;或者在设备掉电的瞬间做一些紧急关机处理&#xff0c;比如保存重要的用户数据&#xff…

【C语言】超详解strncpystrncatstrncmpstrerrorperror的使⽤和模拟实现

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 ✏️真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&am…

jmeter,跨线程调用cookie

结构目录 一、线程组1 1、创建登录的【HTTP请求】&#xff0c;并配置接口所需参数&#xff1b; 2、创建【正则表达式提取器】&#xff0c;用正则表达式提取cookie字段&#xff1b; 3、创建【调试取样器】&#xff0c;便于观察第2步提取出的数据&#xff1b; 4、创建【BeanSh…

【Jenkins】节点 node、凭据 credentials、任务 job

一、节点 node Jenkins在安装并初始化完成后&#xff0c;会有一个主节点&#xff08;Master Node&#xff09;&#xff0c;默认情况下主节点可以同时运行的任务数是2&#xff0c;可以在节点配置中修改&#xff08;系统管理/节点和云管理&#xff09;。 Jenkins中的节点&#…

Redis Helper封装:静态方法

Redis面试题&#xff1a; 1、什么是事务&#xff1f;2、Redis中有事务吗&#xff1f;3、Redis中的事务可以回滚吗&#xff1f; 答&#xff1a; 1、事务是指一个完整的动作&#xff0c;要么全部执行&#xff0c;要么什么也没有做 2、Redis中有事务&#xff0c;Redis 事务不是严…

nodejs+vue+微信小程序+python+PHP血液中心管理平台的设计与实现-计算机毕业设计推荐

实现采血的完整功能&#xff0c;系统用户主要分为两类&#xff0c;一类是管理员&#xff0c;一类是采血工作人员。管理员主要对采血工作人员以及血库进行管理。派发账号给员工作为采血工作人员&#xff0c;对血库的出库入库进行信息化管理。采血工作人员主要完成采血工作。通过…

日志框架Log4j、JUL、JCL、Slf4j、Logback、Log4j2

为什么程序需要记录日志 我们不可能实时的24小时对系统进行人工监控&#xff0c;那么如果程序出现异常错误时要如何排查呢&#xff1f;并且系统在运行时做了哪些事情我们又从何得知呢&#xff1f;这个时候日志这个概念就出现了&#xff0c;日志的出现对系统监控和异常分析起着…

亿赛通电子文档安全管理系统 SQL注入漏洞复现

0x01 产品简介 亿赛通电子文档安全管理系统&#xff08;简称&#xff1a;CDG&#xff09;是一款电子文档安全加密软件&#xff0c;该系统利用驱动层透明加密技术&#xff0c;通过对电子文档的加密保护&#xff0c;防止内部员工泄密和外部人员非法窃取企业核心重要数据资产&…

C语言第五十弹---模拟使用strcat函数

使用C语言模拟使用strcat函数 strcat 函数是 C 标准库中的字符串拼接函数&#xff0c;它用于将一个字符串追加到另一个字符串的末尾。strcat 函数的定义如下&#xff1a;它接受两个参数&#xff0c;第一个参数 dest 是目标字符串的指针&#xff0c;第二个参数 src 是源字符串的…