一、应用场景

大家在使用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动态表名最优方案的探索的更多相关文章

  1. 【react】实现动态表单中嵌套动态表单

    要实现一个功能动态表单中嵌套动态表单如下: 仔细看看antd的文档其实不难 具体步骤如下 1.建立一个 名为 ConcatRegion的组件(动态表单A)代码如下 export function Co ...

  2. mybatis操作动态表+动态字段+存储过程

    存储过程 statementType="CALLABLE" <!-- 计算金额存储过程--> <update id="getCalcDistributo ...

  3. 简易OA漫谈之工作流设计(六,快捷表单和动态表单)

    如果没有表单设计功能,我们一般建物理表,再把表单挂接到流程, 我们可以把外接表单的地址填到表单地址中,地址中会传递一个id. 如果使用外接表单,在审批的时候可能会“不太友好”,因为在审批单上看不到任何 ...

  4. 1-Spark-1-性能调优-数据倾斜1-特征/常见原因/后果/常见调优方案

    数据倾斜特征:个别Task处理大部分数据 后果:1.OOM;2.速度变慢,甚至变得慢的不可接受 常见原因: 数据倾斜的定位: 1.WebUI(查看Task运行的数据量的大小). 2.Log,查看log ...

  5. 循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储

    在我们一些系统里面,有时候会需要一些让用户自定义的数据信息,一般这些可以使用扩展JSON进行存储,不过每个业务表的显示项目可能不一样,因此需要根据不同的表单进行设计,然后进行对应的数据存储.本篇随笔结 ...

  6. Tableview中Dynamic Prototypes动态表的使用

    Tableview时IOS中应用非常广泛的控件,当需要动态的添加多条不同的数据时,需要用动态表来实现,下面给出一个小例子,适用于不确定Section的数目,并且每个Section中的行数也不同的情况, ...

  7. 【翻译】Flink Table Api & SQL —Streaming 概念 ——动态表

    本文翻译自官网:Flink Table Api & SQL 动态表 https://ci.apache.org/projects/flink/flink-docs-release-1.9/de ...

  8. Flink原理(七)——动态表(Dynamic tables)

    前言 本文是结合Flink官网,个人理解所得,若是有误欢迎留言指出,谢谢!文中图皆来自官网(链接[1]). 本文将随着下面这个问题展开,针对该问题更为生动的解释可以参见金竹老师的分享(链接[2]). ...

  9. 2017.2.28 activiti实战--第六章--任务表单(一)动态表单

    学习资料:<Activiti实战> 第六章 任务表单(一)动态表单 内容概览:本章要完成一个OA(协同办公系统)的请假流程的设计,从实用的角度,讲解如何将activiti与业务紧密相连. ...

随机推荐

  1. Vue项目中的接口进阶使用

    创建services文件夹 1.文件夹apis.index.request的三个文件. 2.apis文件放接口 export const apis = { checkDeviceNo: '/api/c ...

  2. Seata源码分析——SessionManager

    目录 事务管理器 SessionManager SessionLifecycleListener AbstractSessionManager 事务存储管理器 RedisTransactionStor ...

  3. CabloyJS自带工作流引擎的文档清单

    文档清单 CabloyJS自带工作流引擎文档已经整理出来,欢迎大家围观.拍砖 介绍 介绍 演示:CMS审批工作流 单元测试用例集 流程定义 基本概念 JSON规范 listener规范 listene ...

  4. Spring Security自定义认证器

    在了解过Security的认证器后,如果想自定义登陆,只要实现AuthenticationProvider还有对应的Authentication就可以了 Authentication 首先要创建一个自 ...

  5. 物联网?快来看 Arduino 上云啦

    作者:HelloGitHub-Anthony 这里是 HelloGitHub 推出的讲解开源硬件开发平台 Arduino 的系列教程. 第一篇:Arduino 介绍和开发环境搭建 第二篇:制作温湿度显 ...

  6. 实现领域驱动设计 - 使用ABP框架 - 创建实体

    用例演示 - 创建实体 本节将演示一些示例用例并讨论可选场景. 创建实体 从实体/聚合根类创建对象是实体生命周期的第一步.聚合/聚合根规则和最佳实践部分建议为Entity类创建一个主构造函数,以保证创 ...

  7. ansible环境安装及数据恢复

    配置免密登录服务器及下载备份文件#!/bin/bash BACKUP=192.168.30.233 #一行写一个IP BACKUP_PASSWD="lxzl_root*#2021" ...

  8. centos7 离线升级/在线升级操作系统内核

    目录 一.前言 二.系统环境 三.系统内核下载网址 四.centos7离线升级系统内核 1.先查看系统环境 2.离线升级系统内核 五.在线升级系统内核 一.前言 CentOS(Community EN ...

  9. React技巧之中断map循环

    正文从这开始~ 总览 在React中,中断map()循环: 在数组上调用slice()方法,来得到数组的一部分. 在部分数组上调用map()方法. 遍历部分数组. export default fun ...

  10. 链表设计与Java实现,手写LinkedList这也太清楚了吧!!!

    链表设计与实现 在谈链表之前,我们先谈谈我们平常编程会遇到的很常见的一个问题.如果在编程的时候,某个变量在后续编程中仍需使用,我们可以用一个局部变量来保存该值,除此之外一个更加常用的方法就是使用容器了 ...