解决SpringMVC使用MyBatis-Plus自定义MyBaits拦截器不生效的问题
自定义MyBatis拦截器
如果是SpringBoot项目引入@Component注解就生效了,但是SpringMVC不行
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.util.StringUtils;import java.lang.reflect.Field;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "update", args = {Statement.class,}),@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})
})
//@Component 在SpringBoot生效,在SpringMVC不生效
public final class MyBatisSqlParsingPlugin implements Interceptor {private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler statementHandler = (StatementHandler) invocation.getTarget();ParameterHandler parameterHandler = statementHandler.getParameterHandler();BoundSql boundSql = statementHandler.getBoundSql();try {String sql = formatSql(parameterHandler, boundSql);if (!boundSql.getSql().equals(sql)) {log.info("Execute SQL:{}", sql);}} catch (Exception e) {String sql = boundSql.getSql();log.error("Execute SQL:{}\nException:", sql, e);}return invocation.proceed();}@Overridepublic Object plugin(Object target) {return Interceptor.super.plugin(target);}@Overridepublic void setProperties(Properties properties) {Interceptor.super.setProperties(properties);}/*** 格式化SQL及其参数*/private String formatSql(ParameterHandler parameterHandler, BoundSql boundSql) throws NoSuchFieldException, IllegalAccessException {Class<? extends ParameterHandler> parameterHandlerClass = parameterHandler.getClass();Field mappedStatementField = parameterHandlerClass.getDeclaredField("mappedStatement");mappedStatementField.setAccessible(true);MappedStatement mappedStatement = (MappedStatement) mappedStatementField.get(parameterHandler);String sql = boundSql.getSql().replaceAll("\\s+", " ");// sql字符串是空或存储过程,直接跳过if (!StringUtils.hasText(sql) || sql.trim().charAt(0) == '{') {return "";}// 不传参数的场景,直接把Sql美化一下返回出去Object parameterObject = parameterHandler.getParameterObject();List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();if (Objects.isNull(parameterObject) || parameterMappingList.isEmpty()) {return sql;}return handleCommonParameter(sql, boundSql, mappedStatement);}//替换预编译SQLprivate String handleCommonParameter(String sql, BoundSql boundSql, MappedStatement mappedStatement) {Object parameterObject = boundSql.getParameterObject();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();Configuration configuration = mappedStatement.getConfiguration();TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();List<String> params = new ArrayList<>();for (ParameterMapping parameterMapping : parameterMappings) {if (parameterMapping.getMode() == ParameterMode.OUT) {continue;}Object propertyValue;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) {propertyValue = boundSql.getAdditionalParameter(propertyName);} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {propertyValue = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);propertyValue = metaObject.getValue(propertyName);}params.add(this.formatParam(propertyValue));}//转译百分号if (sql.contains("%")) {//如果参数不一致直接返回SQLPattern pattern = Pattern.compile("\\?");Matcher matcher = pattern.matcher(sql);int count = 0;while (matcher.find()) {count++;}if (count == 0 || params.isEmpty()) {return sql;}if (params.size() != count) {log.error("SQL:{}", sql);log.error("SQL parameters:{}", params);return sql;}sql = sql.replaceAll("%", "%%");}sql = sql.replaceAll("\\?", "%s");return String.format(sql, params.toArray());}private String formatParam(Object object) {if (object == null) {return "null";}if (object instanceof String) {return formatString((String) object);}if (object instanceof Date) {return formatDate((Date) object);}if (object instanceof LocalDate) {return formatLocalDate((LocalDate) object);}if (object instanceof LocalDateTime) {return formatLocalDateTime((LocalDateTime) object);}return object.toString();}private static String formatString(String str) {return "'" + str + "'";}private String formatDate(Date date) {return "'" + DATE_TIME_FORMATTER.format(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()) + "'";}private String formatLocalDate(LocalDate date) {return "'" + DATE_FORMATTER.format(date) + "'";}private String formatLocalDateTime(LocalDateTime dateTime) {return "'" + DATE_TIME_FORMATTER.format(dateTime) + "'";}
}
mybatis-plus使用mybatis插件
MybatisPlusInterceptor实现的是MyBaits Interceptor接口,而MybatisPlusInterceptor自己又创建了一个InnerInterceptor接口去实现自己的插件,比如:PaginationInnerInterceptor 分页插件。我们使用的是MyBatis Interceptor的插件,所以不能注入到MybatisPlusInterceptor类里面,而是要和MybatisPlusInterceptor平级,因为前面也说了MybatisPlusInterceptor 实现的是MyBaits Interceptor,而我们自定义的MyBatisSqlParsingPlugin实现的也是MyBaits Interceptor,因此要和MybatisPlusInterceptor平级放到plugins下面,建议自定义的MyBaits Interceptor要放到MybatisPlusInterceptor前面,防止MybatisPlusInterceptor干扰我们自定义的插件。
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="mapperLocations" value="classpath*:mapper/**/*.xml"/><property name="typeAliasesPackage" value="com.bsoft.extend.mybatis.entity"/><property name="plugins"><array><!-- 引入的是 MyBatis 插件 --><ref bean="myBatisSqlParsingPlugin"/><!-- 引入的是 MyBatis-Plus 自己封装的插件 --><ref bean="mybatisPlusInterceptor"/></array></property></bean><!-- 实现 MyBatis Interceptor 接口的插件 --><bean id="myBatisSqlParsingPlugin" class="com.bsoft.extend.mybatis.plugins.MyBatisSqlParsingPlugin"/><!-- 实现 MyBatis-Plus InnerInterceptor 接口的插件【就是MyBatis-Plus自己封装的插件】 --><bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor"><property name="interceptors"><list><!-- 引入 MyBatis-Plus 分页插件 --><ref bean="paginationInnerInterceptor"/></list></property></bean><!-- MyBatis-Plus 分页插件配置 --><bean id="paginationInnerInterceptor" class="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor"><constructor-arg name="dbType" value="ORACLE"/><property name="optimizeJoin" value="true"/></bean>
相关文章:
解决SpringMVC使用MyBatis-Plus自定义MyBaits拦截器不生效的问题
自定义MyBatis拦截器 如果是SpringBoot项目引入Component注解就生效了,但是SpringMVC不行 import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.StatementHandler; i…...

Swagger与RESTful API
1. Swagger简介 在现代软件开发中,RESTful API已成为应用程序间通信的一个标准。这种架构风格通过使用标准的HTTP方法来执行网络上的操作,简化了不同系统之间的交互。API(应用程序编程接口)允许不同的软件系统以一种预定义的方式…...

MySQL84 -- ERROR 1524 (HY000): Plugin ‘msql_native_password‘ is not loaded.
【问题描述】 MySQL 8.4版本,配置用户使用mysql_native_password认证插件验证用户身份,报错: 【解决方法】(Windows, MySQL 8.4) 1、修改MySQL配置文件my.ini,在[mysqld]段添加mysql_native_passwordON。 2、管理员…...

将Excel中的错误值#N/A替换成心仪的字符串,瞬间爱了……
常用表格的人都晓得,看到满屏悦动的#N/A,心情都会不好。把它替换成自己心仪的字符,瞬间就爱了。 (笔记模板由python脚本于2024年06月13日 19:32:37创建,本篇笔记适合常用Excel,喜欢数据的coder翻阅) 【学习的细节是欢悦…...

AI大模型日报#0628:谷歌开源9B 27B版Gemma2、AI首次实时生成视频、讯飞星火4.0发布
导读:AI大模型日报,爬虫LLM自动生成,一文览尽每日AI大模型要点资讯!目前采用“文心一言”(ERNIE-4.0-8K-latest)生成了今日要点以及每条资讯的摘要。欢迎阅读!《AI大模型日报》今日要点…...
【随笔】提高代码学习水平(以更高的视角看事物)
最近,我感觉到自己的代码水平似乎卡在了一个瓶颈。似乎只想着数仓,Hive,Spark技术优化,但只要稍微离开这几个点,我就感到无所适从。我开始反思,或许,我应该总结一下自己的学习方法。 1.站的高&…...
游戏AI的创造思路-技术基础-深度学习(5)
继续深度学习技术的探讨,填坑不断,头秃不断~~~~~ 目录 3.5. 自编码器(AE) 3.5.1. 定义 3.5.2. 形成过程 3.5.3. 运行原理 3.5.3.1.运行原理及基本框架 3.5.3.2. 示例代码 3.5.4. 优缺点 3.5.5. 存在的问题和解决方法 3.5…...

基于SpringBoot养老院管理系统设计和实现(源码+LW+调试文档+讲解等)
💗博主介绍:✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码数据库🌟感兴趣的可以先收藏起来,还…...

餐饮点餐的简单MySQL集合
ER图 模型图(没有进行排序,混乱) DDL和DML /* Navicat MySQL Data TransferSource Server : Mylink Source Server Version : 50726 Source Host : localhost:3306 Source Database : schooldbTarget Server Type …...

STM32驱动-ads1112
汇总一系列AD/DA的驱动程序 ads1112.c #include "ads1112.h" #include "common.h"void AD5726_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE );//PORTA、D时钟使能 G…...
数据结构与算法高频面试题
初级面试题及详细解答 当涉及到数据结构与算法的初级面试题时,通常涉及基本的数据结构操作、算法复杂度分析和基本算法的应用。 1. 什么是数组?数组和链表有什么区别? 解答: 数组:是一种线性数据结构,用…...

uni-app的showModal提示框,进行删除的二次确认,可自定义确定或取消操作
实现效果: 此处为删除的二次确认示例,点击删除按钮时出现该提示,该提示写在js script中。 实现方式: 通过uni.showModal进行提示,success为确认状态下的操作自定义,此处调用后端接口进行了删除操作&#…...

5款提高工作效率的免费工具推荐
SimpleTex SimpleTex是一款用于创建和编辑LaTeX公式的简单工具。它能够识别图片中的复杂公式并将其转换为可编辑的数据格式。该软件提供了一个直观的界面,用户可以在编辑LaTeX代码的同时实时预览公式的效果,无需额外的编译步骤。此外,SimpleT…...
区块链的技术架构:节点、网络和数据结构
区块链技术听起来很高大上,但其实它的核心架构并不难理解。今天我们就用一些简单的例子和有趣的比喻,来聊聊区块链的技术架构:节点、网络和数据结构。 节点:区块链的“细胞” 想象一下,区块链就像是一个大型的组织&a…...
pdfmake不能设置表格边框颜色?
找到pdfmake>build>pdfmake.js中: 找到定义的“TableProcessor.prototype.drawVerticalLine”和“TableProcessor.prototype.drawHorizontalLine”两个方法: 重新定义borderColor: var borderColor this.tableNode.table.borderColor||"#…...
laravel 使用RabbitMQ作为消息中间件
先搞定环境,安装amqp扩展 确保已安装rabbitmq-c-dev。 比如 可以使用apk add rabbmit-c-dev安装 cd ~ wget http://pecl.php.net/get/amqp-1.10.2.tgz tar -zxf amqp-1.10.2.tgz cd amqp-1.10.2 phpize ./configure make && make install cd ~ rm -rf am…...

web项目打包成可以离线跑的exe软件
目录 引言打开PyCharm安装依赖创建 Web 应用运行应用程序打包成可执行文件结语注意事项 引言 在开发桌面应用程序时,我们经常需要将网页集成到应用程序中。Python 提供了多种方法来实现这一目标,其中 pywebview 是一个轻量级的库,它允许我们…...

BFS:队列+树的宽搜
一、二叉树的层序遍历 . - 力扣(LeetCode) 该题的层序遍历和以往不同的是需要一层一层去遍历,每一次while循环都要知道在队列中节点的个数,然后用一个for循环将该层节点走完了再走下一层 class Solution { public:vector<vec…...

MySQL高级-SQL优化- count 优化 - 尽量使用count(*)
文章目录 1、count 优化2、count的几种用法3、count(*)4、count(id)5、count(profession)6、count(null)7、 count(1) 1、count 优化 MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回这个数,效率很高&a…...
python Flask methods
在 Flask 中,app.route() 装饰器用于定义 URL 路由和与之关联的视图函数。当你想指定某个 URL 可以接受哪些 HTTP 方法时,你可以使用 methods 参数。methods 是一个列表,它可以包含任何有效的 HTTP 方法。 Falsk文章中的描述: 链…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...