mybatisplus做SQL拦截添加自定义排序

前言

工作中写的一段代码,备个份,以后兴许能直接用

功能描述:如果前端传入了排序规则,则优先按传入的字段进行排序,SQL原有的排序规则追加到末尾

注:我们项目里的分页查询,是基于XML的SQL执行的,没有直接使用mybatis-plus的 IPage

正文

定义拦截器


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.jetbrains.annotations.Nullable;

import java.sql.Connection;
import java.util.List;

/**
 * 添加自定义排序规则
 * 如果前端传入了排序规则,则优先按传入的字段进行排序,SQL原有的排序规则追加到末尾
 * 这个SQL的处理方式【不是基于】字符串拼接,底层的SQL执行是基于 com.mysql.cj.jdbc.ClientPreparedStatement.execute,
 *  PreparedStatement 是防SQL注入的,强行做SQL注入会抛语法错误的异常 - syntax exception
 * @author weiheng
 * @date 2024-01-23
 **/
@Slf4j
public class MybatisPageOrderSqlInterceptor extends JsqlParserSupport implements InnerInterceptor {

    /** 排序方式 - 升序 */
    private static final String SORT_ASC = "asc";

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {

        if (log.isDebugEnabled()) {
            log.info(">>>>> 自定义排序拦截 StatementHandler[{}]", sh);
            log.info(">>>>> 自定义排序拦截 connection[{}]", connection);
            log.info(">>>>> 自定义排序拦截 transactionTimeout[{}]", transactionTimeout);
        }
        
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct != SqlCommandType.SELECT) {
            // 不是查询操作则直接跳过,不做任何操作
            return;
        }   

        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
        // 取分页查询的入参 PageDTO
        PageDTO pageDto = getPageDTO(mpSh);
        if (pageDto == null || CollUtil.isEmpty(pageDto.getOrderColumns())) {
            // 不是自定义分页查询,不做处理
            return;
        }
        mpBs.sql(parserMulti(mpBs.sql(), pageDto));
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        PageDTO dto;
        if (obj instanceof PageDTO) {
            dto = (PageDTO) obj;
        } else {
            // 如果不是分页查询,则不做处理
            return;
        }

        // 1. 解析SQL
        PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
        List<OrderByElement> originOrderList = plainSelect.getOrderByElements();
        List<OrderByElement> clonedOriginOrder = null;
        if (CollUtil.isNotEmpty(originOrderList)) {
            // 创建副本,然后删除原有排序规则
            clonedOriginOrder = BeanUtil.copyToList(originOrderList, OrderByElement.class);
            originOrderList.clear();
        }

        // 2. 向 plainSelect 中添加自定义排序规则
        addCustomizeSortColumns(dto, plainSelect);

        // 3. 添加SQL的原始排序规则 - 追加到末尾
        if (CollUtil.isNotEmpty(clonedOriginOrder)) {
            plainSelect.addOrderByElements(clonedOriginOrder);
        }

        if (log.isDebugEnabled()) {
            log.info("<<<<< 自定义排序拦截 plainSelect[{}]", plainSelect);
        }
    }

    /**
     * 添加自定义排序规则
     *
     * @param dto SQL查询入参
     * @param plainSelect 明文
     * @author weiheng
     **/
    private void addCustomizeSortColumns(PageDTO dto, PlainSelect plainSelect) {
        List<OrderColumnDTO> orderColumns = dto.getOrderColumns();
        for (OrderColumnDTO c : orderColumns) {
            // 构建新的排序规则
            OrderByElement orderByElement = new OrderByElement();
            // 设置排序 - 不传值则默认asc升序
            orderByElement.setAsc(SORT_ASC.equalsIgnoreCase(c.getSort()));
            // 设置排序字段
            orderByElement.setExpression(new Column(c.getOrderColumn()));
            // 重新封装条件 - 优先按自定义进行排序
            plainSelect.addOrderByElements(orderByElement);
        }
    }

    /**
     * 分页查询入参获取
     * 大概搜了下,没有到3个入参的,如果有,请将 PageDTO 放到第1或第2的位置
     * @param mpSh 处理对象
     * @return 分页查询入参
     * @author weiheng
     **/
    @Nullable
    private PageDTO getPageDTO(PluginUtils.MPStatementHandler mpSh) {
        PageDTO pageDto = null;
        Object obj = mpSh.parameterHandler().getParameterObject();
        try {
            Object param = ((MapperMethod.ParamMap<?>) obj).get("param1");
            if (param instanceof PageDTO) {
                pageDto = (PageDTO) param;
            } else {
                param = ((MapperMethod.ParamMap<?>) obj).get("param2");
                if (param instanceof PageDTO) {
                    pageDto = (PageDTO) param;
                }
            }
        } catch (Exception e) {
            // 没有从SQL中获取到对应的参数(没有param1或param2),不走自定义分页逻辑
        }
        return pageDto;
    }

}

添加插件 - 添加自定义的拦截器


import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * mybatis配置
 * @author Heng.Wei
 * @date 2022-05-11
 **/
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor paginationInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // 添加防止全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        // 深度分页插件
        interceptor.addInnerInterceptor(new MybatisHighPageInterceptor());
        // 添加自定义排序
        interceptor.addInnerInterceptor(new MybatisPageOrderSqlInterceptor());
        return interceptor;
    }
}

代码很简单,不多做废话了,自测OK

补充

在这里插入图片描述
从上图可以看到,这里会 for 循环调用所有拦截器的 beforePrepare 方法
然后再通过 invocation.proceed 反射调用 StatementHandler 接口的 prepare 方法
    可以从图中看到接口实现是 PreparedStatementHandler 类的 instantiateStatement 方法

在这里插入图片描述
通过 PreparedStatementHandler 执行SQL操作

PS: 很意外昨晚发的帖子,基本纯代码,也没什么描述和备注,第二天早上看,展现量9、阅读199、新增粉丝2、收藏6、点赞3
感觉这个东西,喜欢看的同学还比较多,所以又稍微【补充】了一下内容

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RedisInsight详细安装教程

简介 RedisInsight 是一个直观高效的 Redis GUI 管理工具&#xff0c;它可以对 Redis 的内存、连接数、命中率以及正常运行时间进行监控&#xff0c;并且可以在界面上使用 CLI 和连接的 Redis 进行交互&#xff08;RedisInsight 内置对 Redis 模块支持&#xff09;。 RedisIn…

第四篇【传奇开心果短博文系列】Python的OpenCV库技术点案例示例:机器学习

传奇开心短博文系列 系列短博文目录Python的OpenCV库技术点案例示例系列短博文 短博文目录一、项目目标二、OpenCV机器学习介绍三、OpenCV支持向量机示例代码四、OpenCV支持向量机示例代码扩展五、OpenCVK均值聚类示例代码六、OpenCVK均值聚类示例代码扩展七、OpenCV决策树示例…

调优 mybatis saveBatch 25倍性能

调优 mybatis saveBatch 25倍性能 最近在压测一批接口&#xff0c;发现接口处理速度慢的有点超出预期&#xff0c;感觉很奇怪&#xff0c;后面定位发现是数据库批量保存这块很慢。 这个项目用的是 mybatis-plus&#xff0c;批量保存直接用的是 mybatis-plus 提供的 saveBatch…

Geogebra绘制正态分布曲线-学习b站何威老师视频

​ 参考资料 GeoGebra系列教程3——GGB与正态分布密度曲线_哔哩哔哩_bilibili 我要开始学习啦&#xff0c;吼吼~~~ 准备工作 https://www.geogebra.org/download 选择GeoGebra 经典 6 详细步骤 设计思路具体操作设计积分区间【a,b】创建滑动条a∈[-5,5]&#xff0c;增量是…

P4学习(七)实验四:Explicit Congestion Notification

目录 一. 实验目的二.前置知识略概三. 实验过程1. Topo2. Egress 三. 实验结果1.启动监听服务端2.发送数据包3.查看h2.log的数据4.Iperf模拟Flood超过门限 四.为什么要在Egress上进行ecn的配置 一. 实验目的 ECN allows end-to-end notification of network congestion without…

Android SeekBar 进度条圆角

先看下效果图&#xff1a; 之前&#xff1a; 优化后&#xff1a; 之前的不是圆角是clip切割导致的 全代码&#xff1a; <SeekBarandroid:layout_width"188dp"android:layout_height"wrap_content"android:background"null"android:focusa…

专门为机器学习开发的jpy语言

这本来是一个为工科教学专门开发的附属品&#xff0c;并不是说Python或Java有多不好&#xff0c;根本上它就是一个Java工程教材&#xff0c;但又要结合人工智能。因此&#xff0c;出现了这样一个包容性的怪胎&#xff0c;可以用python一样的语法与Java一起编写。 没流行起来的一…

一个使用pyqt的word文档查重工具

一个使用pyqt的word文档查重工具 使用场景代码使用截图打包好的软件下载链接结尾 使用场景 有时我们在借鉴一篇文档之后还不想有太多重复&#xff0c;这个时候可以使用这个工具对两个word文档进行对比 代码 import sys from PyQt5.QtWidgets import QApplication, QMainWind…

[RK-Linux] 移植Linux-5.10到RK3399(十)| 配置AP6256模组使能WIFI、BT功能

手上 ROC-RK3399-PC Pro 使用蓝牙 WIFI 模组是 AP6256。 一、AP6256 模组介绍 AP6256是正基科技(AMPAK)推出的一款低成本、低功耗的双模模块,它集成了Wi-Fi和蓝牙功能。这款模块支持SDIO接口,具有以下特点: 1、型号:AP6256 2、接口:SDIO(Secure Digital Input/Outp…

搜维尔科技:【简报】元宇宙数字人赛道,优秀作品赏析《大福太郎》

这次采用亮眼的浅粉做为发色&#xff0c;为了贴合她小警察的身分 给了她一顶特制的警帽&#xff0c;上面有大福的荧光蓝叶片作为标 志&#xff0c;而在配件及裙子上也加入了许多科技元素的小巧思。 学校&#xff1a; 朝阳科技大学&#xff08;台湾&#xff09; 选手&#xff…

排序算法经典模型: 梯度提升决策树(GBDT)的应用实战

目录 一、Boosting训练与预测 二、梯度增强的思想核心 三、如何构造弱学习器和加权平均的权重 四、损失函数 五、梯度增强决策树 六、GBDT生成新特征 主要思想 构造流程 七、梯度增强决策树以及在搜索的应用 7.1 GDBT模型调参 7.1.1 框架层面参数 n_estimators su…

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏1(附项目源码)

本篇最终效果演示 文章目录 本篇最终效果演示系列目录前言环境素材绘制地形 实现人物移动指示显示物品名称源码完结 系列目录 【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏1&#xff08;附项目源码&#xff09; 【制作100个unity游戏之23】实现类似七日杀、森…

申万宏源基于 StarRocks 构建实时数仓

作者 &#xff1a;申万宏源证券 实时数仓项目组 小编导读&#xff1a; 申万宏源证券有限公司是由新中国第一家股份制证券公司——申银万国证券股份有限公司与国内资本市场第一家上市证券公司——宏源证券股份有限公司&#xff0c;于 2015 年 1 月 16 日合并组建而成&#xff0c…

【若依】关于对象查询list返回,进行业务处理以后的分页问题

1、查询对象Jglkq返回 list&#xff0c;对 list 进行业务处理后返回&#xff0c;但分页出现问题。 /*** 嫁功率考勤查询*/RequiresPermissions("hr:kq:list")PostMapping("/list")ResponseBodypublic TableDataInfo list(Jglkq jglkq) throws ParseExcepti…

简单高效 Learn LaTeX 009 - LaTex Cite Notes (30 mins) 引用与注释

这一集里介绍了对文献引用的表示方法&#xff0c;和添加注释文本的方法&#xff1a; https://www.ixigua.com/7298100920137548288?id7304342671428944403&logTag495628805c8329a41ffa

JVM系列-6.java垃圾回收

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理&#x1f525;如果感觉博主的文…

PyTorch深度学习实战(32)——DCGAN详解与实现

PyTorch深度学习实战&#xff08;32&#xff09;——DCGAN详解与实现 0. 前言1. 模型与数据集分析1.1 模型分析1.2 数据集介绍 2. 构建 DCGAN 生成人脸图像小结系列链接 0. 前言 DCGAN (Deep Convolutional Generative Adversarial Networks) 是基于生成对抗网络 (Convolution…

香港代理IP为何受欢迎?

香港代理IP深受用户欢迎的原因主要有以下几点&#xff1a; 1&#xff0c;地理位置优势&#xff1a;香港位于亚洲的中心地带&#xff0c;与中国大陆、东南亚和其他亚洲国家都有良好的网络连接。这使得使用香港代理IP可以实现较快的网络连接速度和较低的延迟&#xff0c;特别是对…

架构篇24:排除架构可用性隐患的利器-FMEA方法

文章目录 FMEA 介绍FMEA 方法FMEA 实战小结 前面的专栏分析高可用复杂度的时候提出了一个问题&#xff1a;高可用和高性能哪个更复杂&#xff0c;根据墨菲定律“可能出错的事情最终都会出错”&#xff0c;架构隐患总有一天会导致系统故障。因此&#xff0c;我们在进行架构设计的…

shopee的AI学习之路——GPTs通过AdInteli 广告变现

GPTs|AdInteli 广告变现 一、什么是 AdInteli AdIntelli 是一个旨在为生成 GPTs 接入广告并实现变现的平台。它连接了全球最大的广告联盟&#xff0c;允许广告商进行竞价&#xff0c;确保展示最有价值的广告。AdIntelli 采用 AI 驱动的收入生成技术&#xff0c;优化广告选择。…