数据分表Mybatis Plus动态表名最优方案的探索
一、应用场景
大家在使用Mybatis进行开发的时候,经常会遇到一种情况:按照月份month将数据放在不同的表里面,查询数据的时候需要跟不同的月份month去查询不同的表。
但是我们都知道,Mybatis是ORM持久层框架,即:实体关系映射,实体Object与数据库表之间是存在一一对应的映射关系。比如:
@Data
public class Student {
private Integer id;
private String stuName;
private Integer age;
}
表结构
CREATE TABLE `student` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`stu_name` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT '姓名',
`age` INT(11) NOT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
)
COMMENT='学生表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
Student 实体类与student表是一一对应的关系,如果我们希望将学员表按照月份进行分表,比如:student_202206、student_202207、student_202208,即产生了一个实体类及其Mapper需要操作多个数据库分月表,这种情况在Mybatis plus下我们该如何操作数据呢? 其实方法有很多,我将我实践中总结出的最优方案给大家进行说明。
二、动态表名处理器接口实现
为了处理上述类似的问题,mybatis plus提供了动态表名处理器接口TableNameHandler,我们只需要实现这个接口,并将这个接口应用配置生效,即可实现动态表名。
需要注意的是:
- 在mybatis plus 3.4版本之前,动态表名处理器接口是
ITableNameHandler, 需要配合mybatis plus分页插件一起使用才能生效。我们这里只介绍3.4版本之后的实现方式。- 在mybatis plus 3.4.3.2 作废该的方式:dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map); 大家如果见到这种方式实现的动态表名,也是过时的实现方法,新版本中该方法已经删除。
经过我一段时间的实践总结,我的实现类如下(基于mybatis plus 3.4.3.2之后的版本):
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import java.util.Arrays;
import java.util.List;
/**
* 按月份参数,组成动态表名
*/
public class MonthTableNameHandler implements TableNameHandler {
//用于记录哪些表可以使用该月份动态表名处理器(即哪些表按月分表)
private List<String> tableNames;
//构造函数,构造动态表名处理器的时候,传递tableNames参数
public MonthTableNameHandler(String ...tableNames) {
this.tableNames = Arrays.asList(tableNames);
}
//每个请求线程维护一个month数据,避免多线程数据冲突。所以使用ThreadLocal
private static final ThreadLocal<String> MONTH_DATA = new ThreadLocal<>();
//设置请求线程的month数据
public static void setData(String month) {
MONTH_DATA.set(month);
}
//删除当前请求线程的month数据
public static void removeData() {
MONTH_DATA.remove();
}
//动态表名接口实现方法
@Override
public String dynamicTableName(String sql, String tableName) {
if (this.tableNames.contains(tableName)){
return tableName + "_" + MONTH_DATA.get(); //表名增加月份后缀
}else{
return tableName; //表名原样返回
}
}
}
大家先对上面的代码有一个基础了解,看了下面的测试过程,再回头看上面的代码中的注释,就比较好理解了。表名处理器写好了之后,我们要让它生效,还需要做如下的配置。配置内容照葫芦画瓢就可以了。需要关注的部分,我都已经给大家添加了注释。
@Configuration
@MapperScan("com.zimug")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
dynamicTableNameInnerInterceptor.setTableNameHandler(
//可以传多个表名参数,指定哪些表使用MonthTableNameHandler处理表名称
new MonthTableNameHandler("student","teacher")
);
//以拦截器的方式处理表名称
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
//可以传递多个拦截器,即:可以传递多个表名处理器TableNameHandler
//interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
return interceptor;
}
}
三、测试实现效果
首先创建一个StudentMapper ,默认情况下StudentMapper 只能操作student表,不能操作student_YYYYMM表。
@Mapper
public interface StudentMapper extends BaseMapper<Student> {}
下面我们来写一个单元测试用例,该测试用例test函数模拟一次请求访问的Controller或者service函数。
@SpringBootTest
class DynamicTableNameTest {
@Resource
private StudentMapper studentMapper;
@Test
void test() {
//执行数据操作之前设置月份(实际场景下该参数从请求参数中解析)
MonthTableNameHandler.setData("202208");
studentMapper.selectById(1); //以id=2查询student_202208这张表
//阅后即焚,将ThreadLocal当前请求线程的数据移除
MonthTableNameHandler.removeData();
}
}
当我们执行这个单元测试用例的时候,我们发现控制台打印出如下信息,注意看SQL的部分,真的是去查询student_202208这张表了,而不是student表。这说明我们的动态表名实现是成功的。

欢迎关注我的公告号:字母哥杂谈,回复003赠送作者专栏《docker修炼之道》的PDF版本,30余篇精品docker文章。字母哥博客:zimug.com
数据分表Mybatis Plus动态表名最优方案的探索的更多相关文章
- 【react】实现动态表单中嵌套动态表单
要实现一个功能动态表单中嵌套动态表单如下: 仔细看看antd的文档其实不难 具体步骤如下 1.建立一个 名为 ConcatRegion的组件(动态表单A)代码如下 export function Co ...
- mybatis操作动态表+动态字段+存储过程
存储过程 statementType="CALLABLE" <!-- 计算金额存储过程--> <update id="getCalcDistributo ...
- 简易OA漫谈之工作流设计(六,快捷表单和动态表单)
如果没有表单设计功能,我们一般建物理表,再把表单挂接到流程, 我们可以把外接表单的地址填到表单地址中,地址中会传递一个id. 如果使用外接表单,在审批的时候可能会“不太友好”,因为在审批单上看不到任何 ...
- 1-Spark-1-性能调优-数据倾斜1-特征/常见原因/后果/常见调优方案
数据倾斜特征:个别Task处理大部分数据 后果:1.OOM;2.速度变慢,甚至变得慢的不可接受 常见原因: 数据倾斜的定位: 1.WebUI(查看Task运行的数据量的大小). 2.Log,查看log ...
- 循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储
在我们一些系统里面,有时候会需要一些让用户自定义的数据信息,一般这些可以使用扩展JSON进行存储,不过每个业务表的显示项目可能不一样,因此需要根据不同的表单进行设计,然后进行对应的数据存储.本篇随笔结 ...
- Tableview中Dynamic Prototypes动态表的使用
Tableview时IOS中应用非常广泛的控件,当需要动态的添加多条不同的数据时,需要用动态表来实现,下面给出一个小例子,适用于不确定Section的数目,并且每个Section中的行数也不同的情况, ...
- 【翻译】Flink Table Api & SQL —Streaming 概念 ——动态表
本文翻译自官网:Flink Table Api & SQL 动态表 https://ci.apache.org/projects/flink/flink-docs-release-1.9/de ...
- Flink原理(七)——动态表(Dynamic tables)
前言 本文是结合Flink官网,个人理解所得,若是有误欢迎留言指出,谢谢!文中图皆来自官网(链接[1]). 本文将随着下面这个问题展开,针对该问题更为生动的解释可以参见金竹老师的分享(链接[2]). ...
- 2017.2.28 activiti实战--第六章--任务表单(一)动态表单
学习资料:<Activiti实战> 第六章 任务表单(一)动态表单 内容概览:本章要完成一个OA(协同办公系统)的请假流程的设计,从实用的角度,讲解如何将activiti与业务紧密相连. ...
随机推荐
- 喜提JDK的BUG一枚!多线程的情况下请谨慎使用这个类的stream遍历。
你好呀,我是歪歪. 前段时间在 RocketMQ 的 ISSUE 里面冲浪的时候,看到一个 pr,虽说是在 RocketMQ 的地盘上发现的,但是这个玩意吧,其实和 RocketMQ 没有任何关系. ...
- 10.Linux防火墙iptables之SNAT与DNAT
Linux防火墙iptables之SNAT与DNAT 目录 Linux防火墙iptables之SNAT与DNAT SNAT策略及应用 SNAT策略概述 SNAT策略典型应用环境 SNAT策略原理 SN ...
- 快速全面了解QT软件界面开发技术
快速全面了解QT软件界面开发技术 目录 前言 一. 学习QT可能的目的是什么? 只想体验一下QT? 当前的项目选择了用QT. 为将来做QT技术储备. 二. QT的核心技术优势是什么? QT在软 ...
- .NET6接入Skywalking链路追踪完整流程
一.Skywalking介绍 Skywalking是一款分布式链路追踪组件,什么是链路追踪? 随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务.互联网应用构建在不同的软 ...
- 你真的会python中的for循环吗
for 循环是 Python 中的通用序列迭代器:它可以单步遍历任何有序序列中的元素.for 语句适用于字符串.列表.元组.其他内置可迭代对象和类创建的新对象. for 通常比 while 循环更容易 ...
- input标签的事件之oninput事件
最近在写前端的时候,用到了oninput事件.(其中也涉及了onclick) 问题:鼠标点击数字和运算符的时候,文本框里的内容到达一定长度时,会出现无法继续往后面跟随光标的问题. 解决:见下面代码 这 ...
- idea显示 RunDashboard ,多个启动项时列表显示
在.idea(项目所在文件夹中)下的workspace.xml文件中找到 <component name="RunDashboard"> 标签,然后添加如下节点 < ...
- MongoDB 的安装和基本操作
MongoDB 的安装 使用 docker 安装 下载镜像: docker pull mongo:4.4.8(推荐,下载指定版本) docker pull mongo:latest (默认下载最新版本 ...
- .NET服务治理之限流中间件-FireflySoft.RateLimit
概述 FireflySoft.RateLimit自2021年1月发布第一个版本以来,经历了多次升级迭代,目前已经十分稳定,被很多开发者应用到了生产系统中,最新发布的版本是3.0.0. Github:h ...
- centos7 yum error yum doesn't have enough cached data
1.vi /etc/resolv.conf,添加下面一行 nameserver 114.114.114.114 修改完成后service network restart进行重启,试一下yum upda ...