【工具】 MyBatis Plus的SQL拦截器自动翻译替换“?“符号为真实数值
【工具】 MyBatis Plus的SQL拦截器自动翻译替换"?"符号为真实数值
使用MyBatis的配置如下所示:
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
调用接口,sql日志打印如下:

参数和sql语句不在同一行显示,需要自己代入思考,不够直观。
实现"?"的自动翻译,步骤如下:
1)编写sql语句拦截器:
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})})
public class SqlStatementInterceptor implements Interceptor {public static final Logger log = LoggerFactory.getLogger("sys-sql");@Overridepublic Object intercept(Invocation invocation) throws Throwable {long startTime = System.currentTimeMillis();try {return invocation.proceed();} finally {long timeConsuming = System.currentTimeMillis() - startTime;log.info("执行SQL:{}ms", timeConsuming);if (timeConsuming > 999 && timeConsuming < 5000) {log.info("执行SQL大于1s:{}ms", timeConsuming);} else if (timeConsuming >= 5000 && timeConsuming < 10000) {log.info("执行SQL大于5s:{}ms", timeConsuming);} else if (timeConsuming >= 10000) {log.info("执行SQL大于10s:{}ms", timeConsuming);}}}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}
2)编写MyBatisPlus日志拦截器:
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;import java.sql.SQLException;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;public class MybatisPlusAllSqlLog implements InnerInterceptor {public static final Logger log = LoggerFactory.getLogger("sys-sql");@Overridepublic void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {logInfo(boundSql, ms, parameter);}@Overridepublic void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameter);logInfo(boundSql, ms, parameter);}private static void logInfo(BoundSql boundSql, MappedStatement ms, Object parameter) {try {log.info("parameter = " + parameter);// 获取到节点的id,即sql语句的idString sqlId = ms.getId();log.info("sqlId = " + sqlId);// 获取节点的配置Configuration configuration = ms.getConfiguration();// 获取到最终的sql语句String sql = getSql(configuration, boundSql, sqlId);log.info("完整的sql:{}", sql);} catch (Exception e) {log.error("异常:{}", e.getLocalizedMessage(), e);}}// 封装了一下sql语句,使得结果返回完整xml路径下的sql语句节点id + sql语句public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {return sqlId + ":" + showSql(configuration, boundSql);}// 进行?的替换public static String showSql(Configuration configuration, BoundSql boundSql) {// 获取参数Object parameterObject = boundSql.getParameterObject();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();// sql语句中多个空格都用一个空格代替String sql = boundSql.getSql().replaceAll("[\\s]+", " ");if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) {// 获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();// 如果根据parameterObject.getClass()可以找到对应的类型,则替换if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(parameterObject)));} else {// MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作MetaObject metaObject = configuration.newMetaObject(parameterObject);for (ParameterMapping parameterMapping : parameterMappings) {String propertyName = parameterMapping.getProperty();if (metaObject.hasGetter(propertyName)) {Object obj = metaObject.getValue(propertyName);sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(obj)));} else if (boundSql.hasAdditionalParameter(propertyName)) {// 该分支是动态sqlObject obj = boundSql.getAdditionalParameter(propertyName);sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(obj)));} else {// 打印出缺失,提醒该参数缺失并防止错位sql = sql.replaceFirst("\\?", "缺失");}}}}return sql;}// 如果参数是String,则添加单引号, 如果是日期,则转换为时间格式器并加单引号; 对参数是null和不是null的情况作了处理private static String getParameterValue(Object obj) {String value;if (obj instanceof String) {value = "'" + obj.toString() + "'";} else if (obj instanceof Date) {DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,DateFormat.DEFAULT, Locale.CHINA);value = "'" + formatter.format(new Date()) + "'";} else {if (obj != null) {value = obj.toString();} else {value = "";}}return value;}}
3)编写MyBatisPlus配置:
@Configuration
public class MybatisConfiguration {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new MybatisPlusAllSqlLog());return mybatisPlusInterceptor;}}
测试:
调用接口,sql日志如下所示:

相关文章:
【工具】 MyBatis Plus的SQL拦截器自动翻译替换“?“符号为真实数值
【工具】 MyBatis Plus的SQL拦截器自动翻译替换"?"符号为真实数值 使用MyBatis的配置如下所示: mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl调用接口,sql日志打印如下: 参数和sql语句不…...
RT-DETR:端到端的实时Transformer检测模型(目标检测+跟踪)
博主一直一来做的都是基于Transformer的目标检测领域,相较于基于卷积的目标检测方法,如YOLO等,其检测速度一直为人诟病。 终于,RT-DETR横空出世,在取得高精度的同时,检测速度也大幅提升。 那么RT-DETR是如…...
OrangePi Kunpeng Pro开发板初体验——家庭小型服务器
引言 在开源硬件的浪潮中,开发板作为创新的基石,正吸引着全球开发者的目光。它们不仅为技术爱好者提供了实验的平台,更为专业开发者带来了实现复杂项目的可能性。本文将深入剖析OrangePi Kunpeng Pro开发板,从开箱到实际应用&…...
AquaCrop农业水资源管理,模拟作物生长过程中水分的需求与消耗
AquaCrop是由世界粮食及农业组织(FAO)开发的一个先进模型,旨在研究和优化农作物的水分生产效率。这个模型在全球范围内被广泛应用于农业水管理,特别是在制定农作物灌溉计划和应对水资源限制方面显示出其强大的实用性。AquaCrop 不…...
爬虫之re数据清洗
文章目录 一、正则【Regular】二、重要语法1、获取内容: 左边(.*?)右边2、替换数据: re.sub(源数据|源数据, 目标数据, 字符串) 一、正则【Regular】 概念: 根据程序员的指示, 从<字符串>中提取数据 结果: 列表 使用频率: 正则跟xpath相比, 正则是弟弟 二、重要语法 …...
惯性动作捕捉与数字人实时交互/运营套装,对高校元宇宙实训室有何作用?
惯性动作捕捉与数字人实时交互/运营套装,可以打破时空限制,通过动捕设备写实数字人软件系统动捕设备系统定制化数字人短视频渲染平台,重塑课程教学方式,开展元宇宙沉浸式体验教学活动和参观交流活动。 写实数字人软件系统内置丰富…...
Leecode---栈---每日温度 / 最小栈及栈和队列的相互实现
栈:先入后出;队列:先入先出 一、每日温度 Leecode—739题目: 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温…...
Linux系统编程——动静态库
目录 一,关于动静态库 1.1 什么是库? 1.2 认识动静态库 1.3 动静态库特征 二,静态库 2.1 制作静态库 2.2 使用静态库 三,动态库 3.1 制作动态库 3.2 使用动态库一些问题 3.3 正确使用动态库三种方法 3.3.1 方法一&…...
json formatter哪个好用
在众多的JSON Formatter工具中,确实有几个相当出色的选择,它们各自拥有独特的特点和优势,可以满足不同用户群体的需求。下面就来为大家推荐几个好用的JSONFormatter工具: 1. JSON Formatter & Validator:这款工具…...
react的hooks是什么意思
React Hooks 是 React 16.8 版本引入的一个新特性,它允许你在不编写类组件的情况下使用状态和其他React特性。Hooks使得函数组件变得更加灵活和强大,因为你可以在其中添加状态逻辑、生命周期方法以及其他React功能。 在传统的React类组件中,…...
AVFrame相关接口(函数)
分配和释放 分配 AVFrame AVFrame *av_frame_alloc(void); 分配一个新的 AVFrame 并返回一个指向它的指针。返回的 AVFrame 需要手动释放。 释放 AVFrame void av_frame_free(AVFrame **frame); 释放由 av_frame_alloc 分配的 AVFrame。这个函数会释放帧的数据并将指针设为 …...
低代码与人工智能的深度融合:行业应用的广泛前景
引言 在当今快速变化的数字化时代,企业面临着越来越多的挑战和机遇。低代码平台和人工智能技术的兴起,为企业提供了新的解决方案,加速了应用开发和智能化转型的步伐。 低代码平台的基本概念及发展背景 低代码平台是一种软件开发方法&#x…...
嵌入式测试基础知识
1.白盒测试也称为结构测试,主要用于检测软件编码过程中的错误。 2.黑盒测试又称为功能测试,主要检测软件的每一个功能是否能够正常使用。 3.软件测试流程:根据测试需求编写测试计划、方案,测试用例,做测试分析&#…...
基于网关的ip频繁访问web限制
一、前言 外部ip对某一个web进行频繁访问,有可能是对web进行攻击,现在提供一种基于网关的ip频繁访问web限制策略,犹如带刀侍卫,审查异常身份人员。如发现异常或者暴力闯关者,即可进行识别管制。 二、基于网关的ip频繁访…...
GSM信令流程(附着、去附着、PDP激活、修改流程)
1、联合附着流程 附着包括身份认证、鉴权等 2、去附着流程 用户发起去附着 SGSN发起去附着 HLR发起去附着 GSSN使用S4发起去附着 3、Activation Procedures(PDP激活流程) 4、PDP更新或修改流程 5、Deactivate PDP Context 6、RAU(Routeing Area Update)流程 7、鉴权加…...
OAK相机如何将 YOLOv10 模型转换成 blob 格式?
编辑:OAK中国 首发:oakchina.cn 喜欢的话,请多多👍⭐️✍ 内容可能会不定期更新,官网内容都是最新的,请查看首发地址链接。 Hello,大家好,这里是OAK中国,我是Ashely。 专…...
【Python】解决Python报错:AttributeError: ‘class‘ object has no attribute ‘xxx‘
🧑 博主简介:阿里巴巴嵌入式技术专家,深耕嵌入式人工智能领域,具备多年的嵌入式硬件产品研发管理经验。 📒 博客介绍:分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向…...
反思 GTC 和 OFC 2024:没有一刀切的方法,但上市时间是关键!
在GTC 2024期间,英伟达宣布了最新的Blackwell B200张量核心GPU,旨在为万亿参数的AI大型语言模型提供支持。Blackwell B200需要先进的800Gbps网络,完全符合在AI工作负载的AI网络报告中概述的预测。随着人工智能工作负载的流量预计每两年增长10…...
速盾:bgp 静态 cdn
BGP(边界网关协议)是一种用于在互联网中交换路由信息的协议,它允许不同自治系统(AS)之间的路由器进行通信和交换路由信息。CDN(内容分发网络)是一种通过将内容分散放置在全球各地的服务器上&…...
union all 以及标量子查询执行计划
SELECT 1, (SELECT ID1 FROM TE WHERE IDA.ID2) FROM .TA A WHERE COLA X UNION ALL SELECT 1, (SELECT ID2 FROM TD WHERE IDA.ID1) FROM .TB A WHERE COLA X UNION ALL SELECT 1,COL2 AS PARENT_UUID FROM .TC a WHERE COLA X 三个union all 看着像是5个table joi…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
