MyBatis Plus 的 InnerInterceptor:更轻量级的 SQL 拦截器

news2025/1/25 1:54:35

  在 Spring Boot 项目中使用 MyBatis Plus 时,你可能会遇到 InnerInterceptor 这个概念。 InnerInterceptor 是 MyBatis Plus 提供的一种轻量级 SQL 拦截器,它与传统的 MyBatis 拦截器(Interceptor)有所不同,具有更简单、更高效的特点,并且更专注于 SQL 执行层面的拦截。本文将详细介绍 InnerInterceptor 的原理、用法和最佳实践,并提供代码示例。

一、为什么需要 InnerInterceptor?

  1. 更轻量级: 相比于传统的 Interceptor,InnerInterceptor 更加轻量级,减少了不必要的拦截开销,提高了性能。
  2. 更专注于 SQL 执行: InnerInterceptor 专注于 SQL 执行层面,可以让你更方便地修改 SQL 语句、参数或结果。
  3. 简化配置: InnerInterceptor 的配置更加简单,无需手动注册,MyBatis Plus 会自动识别并注册。
  4. 易于扩展:你可以通过实现 InnerInterceptor 接口,自定义 SQL 拦截逻辑。
  5. 与 MyBatis Plus 无缝集成:InnerInterceptor 与 MyBatis Plus 的其他功能无缝集成,可以更好地发挥 MyBatis Plus 的优势。
  6. 内置丰富功能: MyBatis Plus 提供了许多内置的 InnerInterceptor 实现,如分页插件、乐观锁插件、SQL性能分析插件等,可以直接使用。

二、InnerInterceptor 与 Interceptor 的区别

  • 拦截范围
    Interceptor 可以拦截 MyBatis 的 Executor、ParameterHandler、ResultSetHandler 和 StatementHandler 等组件,拦截范围更广。
    InnerInterceptor 主要拦截 SQL 执行过程中的 StatementHandler,拦截范围更窄,但更专注于 SQL 执行。
  • 执行时机
    Interceptor 可以拦截 SQL 执行过程中的多个阶段,例如参数处理、SQL 预编译、结果处理等。
    InnerInterceptor 主要拦截 StatementHandler 的 prepare 和 query 方法,更专注于 SQL 语句的准备和执行阶段。
  • 配置方式
    Interceptor 需要在 MyBatis 配置文件或 Spring Bean 中手动注册。
    InnerInterceptor 通过 MyBatis Plus 提供的 MybatisPlusInterceptor 统一注册管理,无需手动注册。
  • 代码复杂度
    Interceptor 的代码相对复杂,需要处理 Invocation 对象,并手动调用 proceed 方法。
    InnerInterceptor 的代码更加简洁,只需要重写对应的方法。
  • 性能
    Interceptor 由于拦截范围更广,可能会带来一定的性能开销。
    InnerInterceptor 由于拦截范围更窄,性能更高。

三、InnerInterceptor 的核心方法

  • void beforePrepare(StatementHandler sh, Connection connection,Integer transactionTimeout): 在 SQL 语句预编译之前调用。
  • void beforeQuery(StatementHandler sh, Connection connection, Integer transactionTimeout): 在 SQL 语句执行之前调用。
  • void afterQuery(StatementHandler sh, Connection connection, Integer transactionTimeout, Object result): 在 SQL 查询执行后调用。
  • void beforeUpdate(StatementHandler sh, Connection connection, Integer transactionTimeout): 在执行 INSERT 或 UPDATE 语句之前调用。
  • void afterUpdate(StatementHandler sh, Connection connection, Integer transactionTimeout,Object result): 在执行 INSERT 或 UPDATE 语句之后调用。

四、实践:使用 InnerInterceptor 修改 SQL 语句

4.1 创建 InnerInterceptor 实现类:

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.io.StringReader;
import java.sql.SQLException;

@Component
@Slf4j
public class MyInnerInterceptor implements InnerInterceptor {

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        String sql = boundSql.getSql();
        try {
            PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
            //sql处理
            String filterSql = addFilterCondition(sql);
            log.info("修改过后的sql:{}", filterSql);
            //修改sql
            mpBs.sql(filterSql);
        } catch (Exception e) {
            log.warn("动态修改sql:{}异常", sql, e);
            throw new SQLException("添加数据权限异常");
        }
    }

    public String addFilterCondition(String originalSql) throws JSQLParserException {
        CCJSqlParserManager parserManager = new CCJSqlParserManager();
        Select select = (Select) parserManager.parse(new StringReader(originalSql));
        PlainSelect plain = (PlainSelect) select.getSelectBody();
        Expression where_expression = plain.getWhere();
        // 这里可以根据需要增加过滤条件
        if (where_expression == null) {
            plain.setWhere(CCJSqlParserUtil.parseCondExpression("age = 35"));
        }
        return plain.toString();
    }
}

4.2 配置 MybatisPlusInterceptor

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.extend.chk.interceptor.MyInnerInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.List;

@Configuration
public class MyBatisPlusConfig {

    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;

    @Autowired
    private MyInnerInterceptor myInnerInterceptor;

    /**
     * 添加Mybatis拦截器
     * 主要是为了保证数据权限拦截器在分页插件拦截器之前执行sql的修改,如果不在这里手动添加的话,PageInterceptor会先执行
     * 先添加的拦截器后执行
     */
    @PostConstruct
    public void addMybatisInterceptor() {
        for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
            org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
            //将数据权限拦截器添加到MybatisPlusInterceptor拦截器链
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            mybatisPlusInterceptor.addInnerInterceptor(myInnerInterceptor);
            //先添加PageHelper分页插件拦截器,再添加MybatisPlusInterceptor拦截器
            //configuration.addInterceptor(new PageInterceptor());
            configuration.addInterceptor(mybatisPlusInterceptor);
        }
    }
}

4.3 使用 InnerInterceptor

  现在,你执行任何 SQL 语句,都会被 InnerInterceptor 拦截,你可以看到 SQL 语句已经被修改。

修改过后的sql:SELECT count(0) FROM t_user_info WHERE age = 35
修改过后的sql:SELECT id, name, password, age, status, last_login_time, token, create_by, create_time, update_by, update_time, remark FROM t_user_info WHERE age = 35 LIMIT ?

五、内置拦截器

  除了自定义拦截器外,MyBatis-Plus 还提供了多个内置拦截器,可以直接使用或作为参考来创建自己的拦截器。以下是几个常用的内置拦截器:

  • PaginationInterceptor:分页插件,支持多种数据库的分页查询。
  • PerformanceAnalyzerInterceptor:性能分析插件,记录每条 SQL 的执行时间和影响行数。
  • OptimisticLockerInterceptor:乐观锁插件,用于防止并发更新时的数据覆盖问题。
  • BlockAttackInterceptor:阻止恶意攻击插件,防止批量删除或更新操作导致数据丢失。

六、常见应用场景

  • SQL 日志记录:如上文所示,记录每次 SQL 执行的时间、参数及结果,便于调试和性能分析。
  • 分页插件:动态地为查询语句添加分页条件,而无需修改原有的 Mapper 文件。
  • SQL 性能监控:统计每条 SQL 的执行次数、平均耗时等指标,帮助识别潜在的性能瓶颈。
  • 缓存实现:基于拦截器实现简单的查询结果缓存,减少不必要的数据库访问。
  • 数据脱敏:在查询结果返回之前,对敏感字段进行加密或替换,确保数据安全。
  • 权限控制:在 SQL 执行前检查用户权限,防止未经授权的操作。

七、最佳实践

  • 按需选择拦截器: 根据实际需求选择合适的拦截器,如果需要修改 SQL 语句、参数或结果,可以使用 InnerInterceptor,如果需要拦截 MyBatis 的其他组件,可以使用 Interceptor。
  • 细粒度控制: 可以根据 MappedStatement 的 ID 或 SQL 语句内容,细粒度控制 InnerInterceptor 的执行范围。
  • 使用内置的 InnerInterceptor: MyBatis Plus 提供了许多内置的 InnerInterceptor 实现,如分页插件、乐观锁插件、SQL 性能分析插件等,可以直接使用,无需重复开发。
  • 避免耗时操作: InnerInterceptor 会在 SQL 执行的关键节点执行,避免在其中执行耗时的操作,以免影响性能。
  • 异常处理: 在 InnerInterceptor 方法中使用 try-catch 代码块处理可能抛出的异常,避免影响正常业务逻辑。
  • 配置顺序: 如果存在多个 InnerInterceptor,Mybatis Plus 会根据 addInnerInterceptor 方法的调用顺序进行执行。
  • 使用 MyBatis Plus 工具类: MyBatis Plus 提供了一些工具类,例如 PluginUtils,可以方便地访问和修改 SQL 语句、参数等信息。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024.1.22 安全周报

政策/标准/指南最新动态 01 工信部印发《关于加强互联网数据中心客户数据安全保护的通知》 原文: https://www.secrss.com/articles/74673 互联网数据中心作为新一代信息基础设施&#xff0c;承载着千行百业的海量客户数据&#xff0c;是关系国民经济命脉的重要战略资源。…

WPS数据分析000005

目录 一、数据录入技巧 二、一维表 三、填充柄 向下自动填充 自动填充选项 日期填充 星期自定义 自定义序列 1-10000序列 四、智能填充 五、数据有效性 出错警告 输入信息 下拉列表 六、记录单 七、导入数据 ​编辑 八、查找录入 会员功能 Xlookup函数 VL…

如何使用 Node.js 构建一个简单的 API?

如何使用 Node.js 构建一个简单的 API&#xff1f; 在现代 Web 开发中&#xff0c;构建高效的 API 是连接前端与后端的核心任务之一。本文将向您展示如何使用 Node.js 构建一个简单的 API&#xff0c;同时通过示例说明如何测试 API。 步骤一&#xff1a;安装 Node.js 和创建项…

StarRocks强大的实时数据分析

代码仓库&#xff1a;https://github.com/StarRocks/starrocks?tabreadme-ov-file StarRocks | A High-Performance Analytical Database 快速开始&#xff1a;StarRocks | StarRocks StarRocks 是一款高性能分析型数据仓库&#xff0c;使用向量化、MPP 架构、CBO、智能物化…

详解Redis的Zset类型及相关命令

目录 Zset简介 ZADD ZCARD ZCOUNT ZRANGE ZREVRANGE ZRANGEBYSCORE ZPOPMAX BZPOPMAX ZPOPMIN BZPOPMIN ZRANK ZREVRANK ZSCORE ZREM ZREMRANGEBYRANK ZREMRANGEBYSCORE ZINCRBY ZINTERSTORE 内部编码 应用场景 Zset简介 有序集合相对于字符串、列表、哈希…

Android实训十 数据存储和访问

实训10 数据存储和访问 一、【实训目的】 1、 SharedPreferences存储数据; 2、 借助Java的I/O体系实现文件的存储&#xff0c; 3、使用Android内置的轻量级数据库SQLite存储数据; 二、【实训内容】 1、实现下图所示的界面&#xff0c;实现以下功能&#xff1a; 1&#x…

在Unity中使用大模型进行离线语音识别

文章目录 1、Vosk下载下载vosk-untiy-asr下载模型在项目中使用语音转文字音频转文字2、whisper下载下载unity项目下载模型在unity中使用1、Vosk 下载 下载vosk-untiy-asr Github链接:https://github.com/alphacep/vosk-unity-asr 进不去Github的可以用网盘 夸克网盘链接:h…

华为支付接入规范

为了确保用户获得良好的支付体验&#xff0c;Payment Kit制定了相关接入设计规范&#xff0c;请开发者遵照执行&#xff0c;具体要求&#xff08;非强制性&#xff09;如下&#xff1a; 一、支付方式呈现 涉及支付公司名称&#xff0c;请统一使用&#xff1a;花瓣支付&#xff…

数据结构——实验八·学生管理系统

嗨~~欢迎来到Tubishu的博客&#x1f338;如果你也是一名在校大学生&#xff0c;正在寻找各种编程资源&#xff0c;那么你就来对地方啦&#x1f31f; Tubishu是一名计算机本科生&#xff0c;会不定期整理和分享学习中的优质资源&#xff0c;希望能为你的编程之路添砖加瓦⭐&…

【C++篇】红黑树封装 实现map和set

目录 前言&#xff1a; 一&#xff0c;库中map和set的大致结构 二&#xff0c;模拟实现 2.1&#xff0c;大致框架 2.2&#xff0c;复用红黑树实现insert接口 2.3&#xff0c;迭代器iterator的实现 operator()的实现&#xff1a; operator--()的实现&#xff1a; 对inser…

解决CentOS9系统下Zabbix 7.2图形中文字符乱码问题

操作系统&#xff1a;CentOS 9 Zabbix版本&#xff1a;Zabbix7.2 问题描述&#xff1a;主机图形中文字符乱码 解决方案&#xff1a; # 安装字体配置和中文语言包 sudo yum install -y fontconfig langpacks-zh_CN.noarch # 检查是否已有中文字体&#xff1a; fc-list :lan…

统计文本文件中单词频率的 Swift 与 Bash 实现详解

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

计算机网络 (57)改进“尽最大努力交付”的服务

前言 计算机网络中的“尽最大努力交付”服务是网络层的一种数据传输方式。这种服务的特点是网络层只负责尽力将数据报从源端传输到目的端&#xff0c;而不保证数据传输的可靠性。 一、标记与分类 为数据分组打上标记&#xff1a; 给不同性质的分组打上不同的标记&#x…

联想电脑怎么设置u盘启动_联想电脑设置u盘启动方法(支持新旧机型)

有很多网友问联想电脑怎么设置u盘启动&#xff0c;联想电脑设置u盘启动的方法有两种&#xff0c;一是通过bios进行设置。二是通过快捷方式启动进入u盘启动。但需要注意有两种引导模式是&#xff0c;一种是uefi引导&#xff0c;一种是传统的leacy引导&#xff0c;所以需要注意制…

Springboot3 自动装配流程与核心文件:imports文件

注&#xff1a;本文以spring-boot v3.4.1源码为基础&#xff0c;梳理spring-boot应用启动流程、分析自动装配的原理 如果对spring-boot2自动装配有兴趣&#xff0c;可以看看我另一篇文章&#xff1a; Springboot2 自动装配之spring-autoconfigure-metadata.properties和spring…

SET alter system reload

目录标题 alter system 只是 写 auto 文件SET & alter system1. **会话级别参数&#xff08;Session-level parameters&#xff09;**2. **系统级别参数&#xff08;System-level parameters&#xff09;**3. **某些特定的超级用户参数**4. **修改时生效的参数**总结&#…

RPC是什么?和HTTP区别?

RPC 是什么&#xff1f;HTTP 是什么&#xff1f; 作为一个程序员&#xff0c;假设我们需要从A电脑的进程发送一段数据到B电脑的进程&#xff0c;我们一般会在代码中使用 Socket 进行编程。 此时&#xff0c;可选性一般就是 TCP 和 UDP 二选一&#xff0c;由于 TCP 可靠、UDP 不…

Y1打卡学习笔记

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客>- **&#x1f356;原作者&#xff1a;K同学啊** yolov5学习 下载源码运行命令查看结果视频检测个人总结 下载源码 地址&#xff1a;https://github.com/ultralytics/yolov5打开cmd后输入&#xff1…

【前端】Hexo 建站指南

文章目录 前言生成站点本地测试部署云端参考 前言 更好的阅读体验&#xff1a;https://blog.dwj601.cn/FrontEnd/Hexo/build-your-own-website-with-hexo/ 笔记记多了&#xff0c;想要分享给同学们一起交流进步&#xff0c;该怎么办&#xff1f;想要搭建一个属于自己的知识库…

【后端开发】字节跳动青训营之Go语言进阶与依赖管理

Go语言进阶与依赖管理 一、Go语言进阶1.1 并发与并行1.2 协程与线程1.3 通道1.3.1 生产消费模型 1.4 并发安全 二、依赖管理 一、Go语言进阶 Go语言一次可以创建上万个协程。 1.1 并发与并行 并发&#xff1a;多程序程序在单核CPU上运行。并行&#xff1a;多程序程序在多核CP…