SpringBoot Mybatis-Plus 日志带参数
SpringBoot Mybatis-Plus 日志带参数
- 1 实现代码
- 2 测试结果
在Spring Boot中,MyBatis插件机制通过拦截器(Interceptor)来实现。拦截器允许开发人员在执行SQL语句的各个阶段(如SQL语句创建、参数处理、结果映射等)插入自定义逻辑。MyBatis的拦截器主要用于增强MyBatis的功能,如日志记录、性能监控、实现分页、数据权限控制、SQL日志、动态SQL生成等,在SQL执行之前后,提供了四个拦截点,在方法执行前后进行处理,支持不同场景的功能扩展。
| 类 | 作用 |
|---|---|
| Executor | 用于执行增/删/改/查等数据库操作。 |
| ParameterHandler | 用于处理 SQL 参数。 |
| ResultSetHandler | 用于处理结果集。 |
| StatementHandler | 用于处理 SQL 语句的创建和参数化。 |
@Signature参数说明
| 参数 | 参数说明 |
|---|---|
| type | 就是指定拦截器类型(Executor, ParameterHandler, StatementHandler, ResultSetHandler)。 |
| method | 是拦截器类型中的方法,不是自己写的方法(update, query, prepare …)。 |
| args | 是method中方法的入参。 |
Executor接口中常见的方法
| 方法 | 实现原理 |
|---|---|
| update | 执行插入、更新和删除操作。 |
| query | 执行查询操作,返回结果集。 |
| flushStatements | 刷新批量执行的 SQL 语句。 |
| commit | 提交事务。 |
| rollback | 回滚事务。 |
| getTransaction | 获取当前事务对象。 |
| close | 关闭执行器。 |
| isClosed | 检查执行器是否已关闭。 |
| clearLocalCache | 清空本地缓存。 |
Mybatis拦截器的实现原理
| 方法 | 实现原理 |
|---|---|
| 注册拦截器 | 拦截器需要在 MyBatis 配置文件中注册,通常是在 mybatis-config.xml 中配置,也可以通过 Java 配置类进行注册。 |
| 实现 Interceptor 接口 | 自定义拦截器需要实现 MyBatis 的 Interceptor 接口,并重写 intercept 方法。这个方法会接收一个 Invocation 对象,表示被拦截的方法调用。 |
| 调用链 | MyBatis 在执行 SQL 操作时,会按照拦截器的配置顺序,依次调用每个拦截器的 intercept 方法。拦截器可以选择在方法执行前后进行处理,或者直接修改方法参数和返回值。 |
@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})
})
public class MyInterceptor implements Interceptor {...
}
1 实现代码
import java.text.DateFormat;
import lombok.extern.slf4j.Slf4j;import cn.hutool.core.collection.CollUtil;import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;import java.sql.Connection;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;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.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;@Slf4j
@Component
@ConditionalOnProperty(prefix = "mybatis-plus", name = "customer-log", havingValue = "true")
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MybatisLogger implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler handler = (StatementHandler) invocation.getTarget();MetaObject object = MetaObject.forObject(handler,SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());MappedStatement statement = (MappedStatement) object.getValue("delegate.mappedStatement");log.info("SQL执行类: {}", statement.getId());log.info("SQL执行类型: {}", statement.getSqlCommandType().toString());BoundSql bound = handler.getBoundSql();Configuration configuration = statement.getConfiguration();String sql = getFullSql(configuration, bound);log.info("SQL语句: {}", sql);long start = System.currentTimeMillis();Object value = invocation.proceed();long end = System.currentTimeMillis();log.info("SQL耗时: {}", (end - start));return value;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 可以通过MyBatis配置文件或注解传递属性}public String getFullSql(Configuration conf, BoundSql bound) {Object object = bound.getParameterObject();List<ParameterMapping> list = bound.getParameterMappings();String sql = bound.getSql().replaceAll("[\\s]+", " ").toLowerCase(Locale.ROOT);if (CollUtil.isNotEmpty(list) && object != null) {TypeHandlerRegistry type = conf.getTypeHandlerRegistry();if (type.hasTypeHandler(object.getClass())) {sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParaValue(object)));} else {MetaObject meta = conf.newMetaObject(object);for (ParameterMapping parameterMapping : list) {String name = parameterMapping.getProperty();if (meta.hasGetter(name)) {Object obj = meta.getValue(name);sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParaValue(obj)));} else if (bound.hasAdditionalParameter(name)) {Object obj = bound.getAdditionalParameter(name);sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParaValue(obj)));} else {sql = sql.replaceFirst("\\?", "缺失");}}}}return sql;}private String getParaValue(Object obj) {if (obj instanceof String) {return "'" + obj + "'";} else if (obj instanceof Date) {DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);return "'" + formatter.format(new Date()) + "'";} else {if (obj != null) {return obj.toString();} else {return "";}}}}
2 测试结果
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@264df4aa]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@d91483c] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@44d84b5a] will not be managed by Spring
2024-07-05 16:32:27.660 INFO 3264 --- [nio-8221-exec-1] com.xu.hander.MybatisLogger : SQL执行类: com.xu.view.mapper.StudentMapper.selectOne
2024-07-05 16:32:27.660 INFO 3264 --- [nio-8221-exec-1] com.xu.hander.MybatisLogger : SQL执行类型: SELECT
2024-07-05 16:32:27.660 INFO 3264 --- [nio-8221-exec-1] com.xu.hander.MybatisLogger : SQL语句: select id,create_by,create_time,create_hour,create_date,update_by,update_time,tenant_id,user_id,cus_id,page,number,is_share,time,event from student where (cus_id = 3 and page = 3 and event = 5) order by create_time desc limit 1
2024-07-05 16:32:27.663 INFO 3264 --- [nio-8221-exec-1] com.xu.hander.MybatisLogger : SQL耗时: 3
相关文章:
SpringBoot Mybatis-Plus 日志带参数
SpringBoot Mybatis-Plus 日志带参数 1 实现代码2 测试结果 在Spring Boot中,MyBatis插件机制通过拦截器(Interceptor)来实现。拦截器允许开发人员在执行SQL语句的各个阶段(如SQL语句创建、参数处理、结果映射等)插入自…...
【WebGIS平台】传统聚落建筑科普数字化建模平台
基于上述概括出建筑单体的特征部件,本文利用互联网、三维建模和地理信息等技术设计了基于浏览器/服务器(B/S)的传统聚落建筑科普数字化平台。该平台不仅实现了对传统聚落建筑风貌从基础到复杂的数字化再现,允许用户轻松在线构建从…...
Zookeeper分布式锁原理说明【简单易理解】
Zookeeper 非公平锁/公平锁/共享锁 。 1.zookeeper分布式锁加锁原理 如上实现方式在并发问题比较严重的情况下,性能会下降的比较厉害,主要原因是,所有的连接都在对同一个节点进行监听,当服务器检测到删除事件时,要通知…...
去除Win32 Tab Control控件每个选项卡上的深色对话框背景
一般情况下,我们是用不带边框的对话框来充当Tab Control的每个选项卡的内容的。 例如,主对话框IDD_TABBOX上有一个Tab Control,上面有两个选项卡,第一个选项卡用的是IDD_DIALOG1充当内容,第二个用的则是IDD_DIALOG2。I…...
iis部署前后端分离项目(React前端,Node.js后端)
iis虽然已经有点过时,但不少用户还在用,故总结一下。 1. 安装iis 如果电脑没有自带iis管理器,打开控制面板->程序->启用或关闭Windows功能,勾选iis安装即可 2. 部署前端项目 打开iis,添加网站,物理…...
【前端项目笔记】9 数据报表
数据报表 效果展示: 在开发代码之前新建分支 git checkout -b report 新建分支report git branch 查看分支 git push -u origin report 将本地report分支推送到云端origin并命名为report 通过路由的形式将数据报表加载到页面中 渲染数据报表基本布局 面包屑导航…...
等保测评推动哈尔滨数字化转型中的安全保障
在数字经济的浪潮下,哈尔滨作为东北老工业基地的核心城市,正积极推动数字化转型,以创新技术驱动产业升级和经济发展。网络安全等级保护测评(简称“等保测评”)作为国家网络安全战略的重要组成部分,为哈尔滨…...
#pragma 指令
#pragma 指令作用是设定编译器的状态或者是指示编译器完成一些特定的动作 message 参数能够在编译信息输出窗口中输出相应的信息 #pragma message(“消息文本”) code_seg参数能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它 #pragma…...
【Excel】 批量跳转图片
目录标题 1. CtrlA全选图片 → 右键 → 大小和属性2. 取消 锁定纵横比 → 跳转高度宽度 → 关闭窗口3. 最后一图拉到最后一单元格 → Alt吸附边框4. CtrlA全选图片 → 对齐 → 左对齐 → 纵向分布!…...
网站更新改版了
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏:Leo杂谈 ✨特色专栏:MySQL学…...
初识大模型
前提:学习一项技术,要从原理、实践、认知三个方面进行攻克。 不懂原理就不会举一反三,走不了太远。 不懂实践就只能纸上谈兵,做事不落地。 认知不高就无法作对决策,天花板太低。 一、知识体系 二、什么是AI 基于机器…...
Open3D SVD算法实现对应点集配准
目录 一、概述 1.1基本思想 1.2实现步骤 二、代码实现 三、实现效果 3.1原始点云 3.2配准后点云 3.3变换矩阵 一、概述 在点云配准中,SVD(Singular Value Decomposition,奇异值分解)方法是一种常用的精确计算旋转和平移变换的算法。其目标是找到一个刚体变…...
bWAPP靶场安装
bWAPP安装 下载 git地址:https://github.com/raesene/bWAPP 百度网盘地址:链接:https://pan.baidu.com/s/1Y-LvHxyW7SozGFtHoc9PKA 提取码:4tt8 –来自百度网盘超级会员V5的分享 phpstudy中打开根目录,并将下载的文…...
SpringBoot + MyBatisPlus 实现多租户分库
一、引言 在如今的软件开发中,多租户(Multi-Tenancy)应用已经变得越来越常见。多租户是一种软件架构技术,它允许一个应用程序实例为多个租户提供服务。每个租户都有自己的数据和配置,但应用程序实例是共享的。而在我们的Spring Boot MyBati…...
【数据挖掘】银行信用卡风险大数据分析与挖掘
银行信用卡风险大数据分析与挖掘 1、实验目的 中国某个商业银行高层发现自家信用卡存在严重的欺诈和拖欠现象,已经影响到自身经营和发展。银行高层希望大数据分析部门采用数据挖掘技术,对影响用户信用等级的主要因素进行分析,结合信用卡用户的人口特征属性对欺诈行为和拖欠…...
使用 Qt 和 ECharts 进行数据可视化
文章目录 示例图表预览折线图散点图柱状图使用 Qt 和 ECharts 进行数据可视化一、准备工作1. 安装 Qt2. 准备 ECharts二、在 Qt 中使用 ECharts1. 创建 Qt 项目2. 配置项目文件3. 在 UI 中添加 WebEngineView4. 加载 ECharts三、创建折线图、散点图和柱状图1. 折线图2. 散点图3…...
【机器学习】在【Pycharm】中的实践教程:使用【逻辑回归模型】进行【乳腺癌检测】
目录 案例背景 具体问题 1. 环境准备 小李的理解 知识点 2. 数据准备 2.1 导入必要的库和数据集 小李的理解 知识点 2.2 数据集基本信息 小李的理解 知识点 注意事项 3. 数据预处理 3.1 划分训练集和测试集 小李的理解 知识点 注意事项 3.2 数据标准化 小李…...
【搭建Nacos服务】centos7 docker从0搭建Nacos服务
前言 本次搭建基于阿里云服务器系统为(CentOS7 Linux)、Nacos(2.0.3)、Docker version 26.1.4 本次搭建基于一个新的云服务器 安装java yum install -y java-1.8.0-openjdk.x86_64安装驱动以及gcc等前置需要的命令 yum install …...
将 build.gradle 配置从 Groovy 迁移到 Kotlin
目录 时间轴 常用术语 脚本文件命名 转换语法 为方法调用添加圆括号 为分配调用添加 转换字符串 重命名文件扩展名 将 def 替换为 val 或 var 为布尔值属性添加 is 前缀 转换列表和映射 配置 build 类型 从 buildscript 迁移到插件块 查找插件 ID 执行重构 转…...
5G(NR) NTN 卫星组网架构
5G(NR) NTN 卫星组网架构 参考 3GPP TR 38.821 5G NTN 技术适用于高轨、低轨等多种星座部署场景,是实现星地网络融合发展的可行技术路线。5G NTN 网络分为用户段、空间段和地面段三部分。其中用户段由各种用户终端组成,包括手持、便携站、嵌入式终端、车…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...
C++--string的模拟实现
一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现,其目的是加强对string的底层了解,以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量,…...
大数据驱动企业决策智能化的路径与实践
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:数据驱动的企业竞争力重构 在这个瞬息万变的商业时代,“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...
