MyBatis Plus应用(四)动态表的实践

MyBatisPlus
placeholder image
admin 发布于:2022-07-03 12:20:40
阅读:loading

1.基本介绍

动态表的目的是为了拆分表的数据,将一个业务功能表中的数据拆分为多个表中存储,将数据分散存储提高查询速度和数据维护。在普通的MyBatis中若要实现动态表则需要自己编写动态表的规则配合再配合$表达式结合使用,MyBatis Plus中提供的动态表功能择是通过全局的配置实现,即声明要动态出现的表名以及构造动态表名的处理逻辑即可,本站系统的实现小试了基于日期规则的动态表名实现,本篇文章则是实现基于当前日期来实现的动态表的数据逻辑,具体参考如下文所述。

2.声明动态表

动态表的声明与分页插件一样需要全局进行配置,声明的是动态表的字符串key和当SQL语句中出现此key文本数据时的对应转换逻辑,其内部实现大概可以理解为对字符串的特殊处理,检索待执行SQL语句中是否包含有待处理的表名字符串,当出现时再根据彼时的参数对象中获取解析逻辑中需要获取的日期参数,调用动态表名的处理规则来获取匹配逻辑的表名,最后再进行字符串替换,达到动态表名的目的,当然了,这一切都是基于使用时的猜测和个人经验的设想,未做更深入的分析,个人拙见,参考配置代码逻辑如下:

package cn.chendd.mybatisplus.config;

import ...;

/**
 * MybatisPlusConfig
 *
 * @author chendd
 * @date 2022/7/3 20:40
 */
@Configuration
public class MybatisPlusConfig {

    /**
     * 分页插件配置/动态表名规则设置
     * @return 插件配置
     */
    @Bean
    public PaginationInterceptor getPaginationInterceptor(){
        PaginationInterceptor interceptor = new PaginationInterceptor();
        //构建动态标名解析器
        DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
        Map<String, ITableNameHandler> tableNameMap = new HashMap<>();
        tableNameMap.put("Sys_operationlog", (metaObject, sql, tableName) -> {
            RoutingStatementHandler handler = (RoutingStatementHandler) metaObject.getOriginalObject();
            BoundSql boundSql = handler.getBoundSql();
            Object parameterObject = boundSql.getParameterObject();
            if(parameterObject instanceof SysOperationLog) {
                SysOperationLog sysOperationLog = (SysOperationLog) parameterObject;
                //日志表的分表规则:Sys_operationlog_202207
                return sysOperationLog.getTableNameCondition();
            }
            Object param = handler.getBoundSql().getParameterObject();
            if (param instanceof DynamicTableName) {
                DynamicTableName dynamicTableName = (DynamicTableName) param;
                return dynamicTableName.getTableNameCondition();
            } else if (param instanceof MapperMethod.ParamMap) {
                MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) handler.getBoundSql().getParameterObject();
                Set<Map.Entry<String , Object>> set = paramMap.entrySet();
                for (Map.Entry<String, Object> entry : set) {
                    Object value = entry.getValue();
                    if(value instanceof DynamicTableName) {
                        DynamicTableName dynamicTableName = (DynamicTableName) value;
                        return dynamicTableName.getTableNameCondition();
                    }
                }
            }
            /*QueryWrapper queryWrapper = (QueryWrapper) paramMap.get("ew");
            System.err.println(queryWrapper.getParamNameValuePairs());
            System.err.println(sql);
            System.err.println(tableName);*/
            return tableName;
        });
        dynamicTableNameParser.setTableNameHandlerMap(tableNameMap);
        interceptor.setSqlParserList(Lists.newArrayList(dynamicTableNameParser));
        return interceptor;
    }

}

3.测试代码

本次的测试代码提供的按日期处理动态表名的规则,当tableDate字段为空时为原始的表名,反之不为空时表名增加动态规则,动态表名为:原始表名 + 下划线 + 日期的年月。本次提供4个测试案例,分别是(1)对象保存时无动态表名规则字段的数据,操作原始表;(2)对象保存时同时提供动态表名参数,但无手写SQL语句,由框架内置提供的处理实现,可按日期生成动态表名;(3)定义Mapper文件,编写UPDATE语句,同时提供动态表名字段实现动态表逻辑;(4)定义Mapper函数和新的参数对象提供新的动态表规则,实现新的规则实现;

package cn.chendd.mybatisplus;

import ...;

import javax.annotation.Resource;

/**
 * 动态表测试
 *
 * @author chendd
 * @date 2022/7/3 19:17
 */
public class DynamicTableTest extends BaseBootstrapTest {

    @Resource
    private SysOperationLogService service;

    @Test
    public void testTable() {
        SysOperationLog entity = new SysOperationLog();
        entity.setUserId(1001L);
        entity.setUserName("chendd");
        //不设置tableDate字段表示操作原始表
        this.service.save(entity);
    }

    @Test
    public void testInsertDynamicTable() {
        SysOperationLog entity = new SysOperationLog();
        entity.setUserId(1001L);
        entity.setUserName("chendd");
        //设置tableDate字段表示操作转换日期后的表
        entity.setTableDate("2022-07-03");
        this.service.save(entity);
    }

    @Test
    public void testUpdateDynamicTable() {
        SysOperationLog entity = new SysOperationLog();
        entity.setUserId(1001L);
        entity.setUserName("chendd");
        //设置tableDate字段表示操作转换日期后的表
        entity.setTableDate("2022-07-03");
        this.service.updateSysOperationLog(entity);
    }

    @Test
    public void testCustomDynamicTable() {
        SysOperationLogParam param = new SysOperationLogParam();
        //设置动态表为拼接新的表后缀
        param.setTableSuffix("_666");
        this.service.querySysOperationLog(param);
    }

}

4.运行结果

image.png

(测试1 操作原始表)

image.png

(测试2 面向对象的动态表规则)

image.png

(测试3 基于Mapper的update动态表)

image.png

(测试4 基于Mapper的自定义规则)

5.知识点细节

(1)可配置多个动态表的规则,每种规则自己实现;

(2)支持面向对象操作时的动态表,也支持Mapper文件中的自定义SQL的动态表;

(3)本例中通过执行SQL时的参数对象中解析动态表的规则,若参数值为空返回原始表,否则替换为动态表;若存在解析动态表的规则,但表名在SQL中并不是独立的“字符块”时仍然不会被视为动态表的替换,此处的字符串作者表达的是类似正则中的\w+修饰匹配逻辑,在Mapper中若想定义的SQL表名不被替换为动态表,在MySQL中可使用 `tableName` 修饰表,或者增加schema修饰表名,此时若存在动态表规则的值也不会被视为动态表;


 点赞


 发表评论

当前回复:作者

 评论列表


留言区