首页 > 其他分享 >数据分表——使用 Mybatis-Plus插件实现动态表名分表(按年份分表、按月份分表)

数据分表——使用 Mybatis-Plus插件实现动态表名分表(按年份分表、按月份分表)

时间:2024-07-02 21:31:35浏览次数:1  
标签:插件 tableName Mybatis handler 表名 分表 import com

本博客适合Mybatis-Plus3.4以上版本,笔者使用版本为3.5.3。


分库与分表的原因

1. 业务场景:日志、交易流水表或者其他数据量大的表,通过日期进行了水平分表,需要通过日期参数,动态的查询数据。
实现思路:利用MybatisPlus的动态表名插件DynamicTableNameInnerInterceptor ,实现Sql执行时,动态的修改表名。
2. 非必须勿使用分库分表:如数据库确实成为性能瓶颈时,在设计分库分表方案时应充分考虑方案的扩展性,或者考虑采用成熟热门的分布式数据库解决方案,如 TiDB。TiDB 数据库,针对 TiKV 中数据的打散,是基于 Range 的方式进行,将不同范围内的[StartKey,EndKey)分配到不同的 Region 上。(目前属实看不懂这最后一句话。。。。)
以上摘自参考博客1

参考博客如下:
参考博客1:mybatis-plus小技能: 分表策略(按年分表和按月分表)
参考博客2:数据分表Mybatis Plus动态表名最优方案的探索

1、分表策略

1.1 在数据库预先创建好按各年份或者月份的分的数据表

1.2 实现动态表名接口

如果动态表名接口在使用时没有赋值,默认操作的是服务器时间当前月份或者年份的表。
1. 实现年份动态表名处理器(YearTableNameHandler)
在你所要使用的SpringBoot工程中创建config配置类包,并在该包下建立handler包用来放置一会儿年份动态表名处理器,起名为YearTableNameHandler.java,使用该类实现TableNameHandler接口并重写dynamicTableName方法。具体模板程序如下:

package com.my.demo17.config.handler;

import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;

/**
 * Classname: YearTableNameHandler
 * Package: com.my.demo17.config.handler
 * Description:
 * 按年份参数,组成动态表名
 * @Author Alex Liang
 * @Create 2024/7/2 下午8:06
 * @Version 1.0
 */

public class YearTableNameHandler implements TableNameHandler {
    /**
     * 用于记录哪些表可以使用该年份动态表名处理器(即哪些表按年份分)
     */
    private List<String> tableNames;
    //

    /**
     * 构造函数,构造动态表名处理器的时候,传递tableName参数
     * @param tableNames
     */
    public YearTableNameHandler(String ...tableNames) {
        this.tableNames = Arrays.asList(tableNames);
    }

    /**
     * 每个请求线程维护一个年份数据,避免多线程数据冲突,所以使用ThreadLocal
     */
    private static final ThreadLocal<String> YEAR_DATA = new ThreadLocal<>();

    /**
     * 设置请求线程的年份数据
     * @param yearData
     */
    public static void setYearData(String yearData) {
        YEAR_DATA.set(yearData);
    }

    /**
     * 删除当前请求线程的年份数据
     */
    public static void removeYearData(){
        YEAR_DATA.remove();
    }


    /**
     * 动态表名接口实现方法
     * @param sql
     * @param tableName
     * @return
     */
    @Override
    public String dynamicTableName(String sql, String tableName) {
        if (this.tableNames.contains(tableName)) {
            if (YEAR_DATA.get() == null) {
                LocalDate date = LocalDate.now();
                return tableName + "_" + date.format(DateTimeFormatter.ofPattern("yyyy")); // 表名增加年份后缀
            }
            return tableName + "_" + YEAR_DATA.get(); // 表名增加年份后缀
        }else {
            return tableName; // 表名原样返回
        }
    }
}

2. 实现月份动态表名处理器(YearTableNameHandler)
类似上一步骤在handler包中建立月份动态表名处理器,起名为MonthTableNameHandler.java,使用该类实现TableNameHandler接口并重写dynamicTableName方法。具体模板程序如下:

package com.my.demo17.config.handler;

import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;

/**
 * Classname: MonthTableNameHandler
 * Package: com.my.demo17.config.handler
 * Description:
 *
 * @Author Alex Liang
 * @Create 2024/7/2 下午8:18
 * @Version 1.0
 */

public class MonthTableNameHandler implements TableNameHandler {
    /**
     * 用于记录哪些表可以使用该月份动态表名处理器(即哪些表按月分表)
     */
    private List<String> tableNames;
    //

    /**
     * 构造函数,构造动态表名处理器的时候,传递tableName参数
     * @param tableNames
     */
    public MonthTableNameHandler(String ...tableNames) {
        this.tableNames = Arrays.asList(tableNames);
    }

    /**
     * 每个请求线程维护一个月份数据,避免多线程数据冲突,所以使用ThreadLocal
     */
    private static final ThreadLocal<String> MONTH_DATA = new ThreadLocal<>();

    /**
     * 设置请求线程的月份数据
     * @param yearData
     */
    public static void setMonthData(String yearData) {
        MONTH_DATA.set(yearData);
    }

    /**
     * 删除当前请求线程的月份数据
     */
    public static void removeMonthData(){
        MONTH_DATA.remove();
    }


    /**
     * 动态表名接口实现方法
     * @param sql
     * @param tableName
     * @return
     */
    @Override
    public String dynamicTableName(String sql, String tableName) {
        if (this.tableNames.contains(tableName)) {
            if (MONTH_DATA.get() == null) {
                LocalDate date = LocalDate.now();
                return tableName + "_" + date.format(DateTimeFormatter.ofPattern("MM")); // 表名增加月份后缀
            }
            return tableName + "_" + MONTH_DATA.get(); // 表名增加月份后缀
        }else {
            return tableName; // 表名原样返回
        }
    }
}

1.3 配置类添加动态表名

创建好年份、月份动态表名处理器后,在config包创建MyBatisPlusConfig.java作为MyBatis-Plus类,在该类中我们要添加刚刚写好的年份、月份动态表名处理器,模板代码如下:

package com.my.demo17.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.my.demo17.config.handler.MonthTableNameHandler;
import com.my.demo17.config.handler.YearTableNameHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Classname: MyBatisPlusConfig
 * Package: com.my.demo17.config
 * Description:
 *
 * @Author Alex Liang
 * @Create 2024/7/2 下午8:03
 * @Version 1.0
 */
@Configuration
// @MapperScan("com.my.demo17.mapper") // 如果在运行主类配置过此注解,则不需要再该类中配置此注解
public class MyBatisPlusConfig {
    @Bean //令方法创建对象并交给Spring去管理
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        DynamicTableNameInnerInterceptor dynamicTableNameMonthInterceptor = new DynamicTableNameInnerInterceptor();
        dynamicTableNameMonthInterceptor.setTableNameHandler(
                 // 可以传多个表名参数,指定哪些表使用MonthTableNameHandler处理表名称
                 new MonthTableNameHandler("student")
        );
        // 以拦截器的方式处理表名称
        interceptor.addInnerInterceptor(dynamicTableNameMonthInterceptor);


        DynamicTableNameInnerInterceptor dynamicTableNameYearInterceptor = new DynamicTableNameInnerInterceptor();
        dynamicTableNameYearInterceptor.setTableNameHandler(
                // 可以传多个表名参数,指定哪些表使用YearTableNameHandler处理表名称
                new YearTableNameHandler("student")
        );
        // 以拦截器的方式处理表名称(也可以传递多个拦截器,即:可以传递多个表名处理器TableNameHandler,
        //                                              如以下代码便是在添加月表名拦截器的基础上又加了年表名拦截器)
        interceptor.addInnerInterceptor(dynamicTableNameYearInterceptor);
        return interceptor;
    }
}

1.4 测试

开始测试之前,一定要确保连接的数据库中已经创建了相应的分表,比如我这里测试的分表分别为按年份的student202407和按月份的student07,否则会报错。

package com.my.demo17;

import com.my.demo17.config.handler.MonthTableNameHandler;
import com.my.demo17.config.handler.YearTableNameHandler;
import com.my.demo17.mapper.StudentMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Demo17ApplicationTests {

    @Autowired
    private StudentMapper studentMapper;
    @Test
    void contextLoads() {
        // 执行之前设置月份(实际场景下,该参数从请求参数中解析)
        MonthTableNameHandler.setMonthData("202407");
        studentMapper.selectById("01");

        MonthTableNameHandler.removeMonthData();
        // 执行之前设置月份(实际场景下,该参数从请求参数中解析)
        YearTableNameHandler.setYearData("2024");
        studentMapper.selectById("01");
        YearTableNameHandler.removeYearData();

    }

}

测试结果如下:
image

标签:插件,tableName,Mybatis,handler,表名,分表,import,com
From: https://www.cnblogs.com/Alex-goforit/p/18280572

相关文章

  • Eplan插件 - 矩形修订云线
    前言在CAD中,矩形云线一直是设计师们用于标注修订区域或突出重要部分的得力工具。然而,在Eplan中,没有直接绘制矩形云线的功能。为了填补这一空白,开发了专门用于Eplan的矩形修订云线插件。这款插件保留了Eplan绘制的习惯,可以简洁快速的框选出需要修订或者重点关注的区域,使整个......
  • MyBatis是什么以及为什么需要ORM框架、快速搭建
    MyBatis是什么MyBatis的前身是Ibatis,本质是一款半自动化的ORM框架,除了能对POJO进行ORM映射之外,还可以编写SQL脚本语句。主要是为了解决我们平时开发中经常写的JDBC代码,将繁琐的JDBC代码封装起来,化繁为简。MyBatis映射文件四要素:1.SQL语句2.映射规则3.POJO4.Mapper接口为......
  • sqlsugar 分表
    一、首字母分表安装hyjiacan.pinyin4net>dotnetaddpackagehyjiacan.pinyin4net--version4.1.1创建分表服务///<summary>///Apricot分表///</summary>publicclassApricotSplitTableService:ISplitTableService{///<summary>///sqlsugar......
  • Temu有哪些选品技巧和方法?Temu选品助手插件
    对于电商来说,选品至关重要,正所谓“三分靠产品,七分靠选品”。好的选品就是那些具有市场竞争力的优质产品,能提升消费者的购物体验,也能帮助卖家实现销售增长和利润提升。今天小编来整理一下有关Temu平台的选品技巧和方法,觉得有用就点赞收藏哦!Temu有哪些选品技巧和方法?1、行业......
  • 关于 VuePress 的插件
    07.插件插件就好比第三方功能,例如增加一个阅读进度条、增加光标效果等。VuePress官网对插件的介绍:插件通常会为VuePress添加全局功能。这里简单介绍几个本站用的插件吧!‍‍插件就好比第三方功能,例如增加一个阅读进度条、增加光标效果等,VuePress官网对插件的介绍:插件通常......
  • Java高手的30k之路|面试宝典|精通MyBatis(一)
    基础知识架构图MyBatis的基本架构图,展示MyBatis核心组件及其相互关系:Configuration......
  • MyBatis-利用切面实现公有字段自动填充(非MyBatisPlus方式)
    需求:在MyBatis框架中,如何对createBy,createTime,updateBy等这些公有字段实现自动填充呢?网上搜了很多,实现的方案全是采用集成MyBatisPlus,利用其封装好的方法来实现的。。。。。数据准备:1、准备一张数据库表CREATETABLE`user`(`id`varchar(36)NOTNULL,`username`......
  • MyBatis-Plus 概括
    1.MyBatis-Plus入门    1.1数据库准备DROPTABLEIFEXISTSuser;CREATETABLEuser(  idBIGINT(20)NOTNULLCOMMENT'主键ID',  nameVARCHAR(30)NULLDEFAULTNULLCOMMENT'姓名',  ageINT(11)NULLDEFAULTNULLCOMMENT'年龄'......
  • MyBatis中Where标签:揭秘高效SQL构建的秘密
    哈喽,大家好,我是木头左!理解Where标签的基础概念在MyBatis中,<where>标签是用于构建SQL查询语句中的一个非常重要的元素。它允许你在一个动态的SQL语句中添加WHERE子句,而不需要担心SQL语法错误或额外的逗号和AND/OR关键字。使用<where>标签可以让的SQL语句更加简洁、易读,并且能够......
  • MybatisPlus学习-2
    增删改id生成策略@TableId(Type=IdType.)auto自增noneinput数据库不要自增,代码中需要指定idassign_ID雪花数据库bigint占位1时间戳41机器码(5+5)序列号12assign_UUID全局配置:配置idtypetable-prefix:tb1_save:dao.save(domain)多条删除deletebatchids......