解决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文章中的描述: 链…...
从样式覆盖到版本升级:全面解析Antd表格固定列对齐问题的解决路径
1. 问题复现:当Antd表格固定列开始"跳舞" 第一次遇到Antd表格固定列错位问题时,我正喝着咖啡调试一个后台管理系统。突然发现表格右侧的固定列像被施了魔法——表头和内容列完全错开,活像跳着蹩脚的探戈。这种问题在Antd 3.x版本中…...
思考的快与慢:模型的“即时回答”与“深思熟虑”
上一篇文章我们学会了如何“使唤”模型:同步、异步、批量、流式,一通操作下来,你已经是调接口的好手了。但很快你可能会发现一个有意思的现象——同样是回答问题,有时候模型快到几乎零秒响应,有时候却要停顿好几秒甚至…...
[已解决]Vscode插件Keil Assistant连接Keil后出现的头文件路径无法寻找问题
问题详情 按照网络上的教程按照并且配置好vscode的Keil Assistant插件后,成功打开了Keil工程并且编译成功。但是头文件无法跳转,以及出现红色波浪线报错。 解决方法 在.vscode\c_cpp_properties.json中添加以下两行路径: "includePath&q…...
智能体架构实战:从LangGraph状态机到多智能体协作
1. 从理论到实践:为什么我们需要一个“智能体架构大全”项目如果你在过去一年里关注过AI领域,尤其是大语言模型的应用开发,那么“智能体”这个词一定已经听得耳朵起茧了。从能帮你写代码的Devin,到能自主完成复杂任务的GPT-4o&…...
NVIDIA显卡终极调校指南:用Profile Inspector释放游戏潜能的简单方法
NVIDIA显卡终极调校指南:用Profile Inspector释放游戏潜能的简单方法 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector 还在为游戏卡顿、画面撕裂而烦恼吗?NVIDIA Profile Inspect…...
从ADI收购LTC看电源管理趋势:软件定义电源与能量收集技术解析
1. 从一笔天价收购案,看电源管理技术的未来十年2016年,模拟芯片行业发生了一场地震级的并购:模拟巨头亚德诺半导体(Analog Devices Inc., ADI)以148亿美元的天价,收购了以高性能模拟芯片闻名的凌力尔特&…...
VLSI时代下74系列离散逻辑芯片的现代应用与设计实践
1. 从“胶水逻辑”到“系统粘合剂”:离散逻辑芯片的现代生存法则 在今天的数字电路设计领域,提起“7400系列”或者“74HC04”,很多年轻工程师的第一反应可能是博物馆里的古董,或者教科书上的历史章节。主流叙事已经被SoC、FPGA和高…...
FPGA上做图像压缩,别从零造轮子!聊聊DCT那些开源IP核与设计技巧
FPGA图像压缩实战:DCT开源IP核选型与架构优化指南 在嵌入式视觉系统开发中,JPEG图像压缩是FPGA工程师经常遇到的需求场景。当项目周期紧张且资源有限时,明智的开发者会优先考虑利用经过验证的开源IP核,而非从零开始实现离散余弦变…...
别再用重启就丢数据的流量统计了!OpenWrt上nlbwmon的持久化配置与性能优化全攻略
OpenWrt高级流量监控:nlbwmon持久化配置与性能优化实战 每次重启路由器后流量统计归零?图表加载慢到怀疑人生?这些问题困扰着许多OpenWrt用户。本文将带你深入解决nlbwmon的两大核心痛点——数据持久化和界面响应速度,打造一个真正…...
告别激活烦恼:KMS_VL_ALL_AIO智能激活脚本的终极解决方案
告别激活烦恼:KMS_VL_ALL_AIO智能激活脚本的终极解决方案 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 你是否曾经为Windows系统激活而烦恼?或者为Office办公软件的激活…...
