当前位置: 首页 > news >正文

mybatis-plus使用拦截器实现sql完整打印

shigen坚持更新文章的博客写手,擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长,分享认知,留住感动。
个人IP:shigen

在使用mybatis-plus(mybatis)的时候,往往需要打印完整的sql语句,然而输出的日志不是很理想:

sql插入

sql查询

因为sql语句中的关键字段信息都是用?来代替的。那有什么方法实现完整的sql打印呢?有是有的,我记得IDEA的插件市场有一款插件可以实现完整sql的打印,但是好像是要收费的。今天刷某音的时候看到了某博主分享了一下自己写了一个拦截器实现了sql完整的打印,以下是实现的效果:
sql完整的打印

可以看到了sql的执行时间和完整的sql语句。sql的执行时间没啥好说的,关键是sql语句的完整打印。现在先来分享一下代码吧。

代码

controller的设计

这里仅展示关键的代码,一个更新的操作,一个分页查询的操作。

    @PostMapping(value = "update")public Result<String> update(@RequestBody @Validated(value = UpdateGroup.class) User user) {int update = userMapper.updateById(user);return update > 0 ? Result.ok(null) : Result.err(null);}@GetMapping(value = "get")public Result<List<User>> get(@RequestParam(value = "id", required = false) Integer id,@RequestParam(value = "name", required = false) String name) {LambdaQueryWrapper<User> queryChainWrapper = new LambdaQueryWrapper<>();queryChainWrapper.eq(id != null, User::getId, id);queryChainWrapper.eq(name != null, User::getUsername, name);List<User> records = userMapper.selectPage(new Page<User>(0, 10), queryChainWrapper).getRecords();return Result.ok(records);}
拦截器设计

虽然这里是mybatis-plus框架,但是还是需要使用到mybatis的功能。

/*** @author shigenfu* @date 2024/6/16 10:01*/
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Slf4j
public class SqlInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 统计sql执行耗时long startTime = System.currentTimeMillis();Object proceed = invocation.proceed();long endTime = System.currentTimeMillis();String printSql = null;try {printSql = generateSql(invocation);} catch (Exception exception) {log.error("获取sql异常", exception);} finally {long costTime = endTime - startTime;log.info("\n 执行SQL耗时:{}ms \n 执行SQL:{}", costTime, printSql);}return proceed;}private static String generateSql(Invocation invocation) {MappedStatement statement = (MappedStatement) invocation.getArgs()[0];Object parameter = null;if (invocation.getArgs().length > 1) {parameter = invocation.getArgs()[1];}Configuration configuration = statement.getConfiguration();BoundSql boundSql = statement.getBoundSql(parameter);// 获取参数对象Object parameterObject = boundSql.getParameterObject();// 获取参数映射List<ParameterMapping> params = boundSql.getParameterMappings();// 获取到执行的SQLString sql = boundSql.getSql();// SQL中多个空格使用一个空格代替sql = sql.replaceAll("[\\s]+", " ");if (!ObjectUtils.isEmpty(params) && !ObjectUtils.isEmpty(parameterObject)) {// TypeHandlerRegistry 是 MyBatis 用来管理 TypeHandler 的注册器 TypeHandler 用于在 Java 类型和 JDBC 类型之间进行转换TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();// 如果参数对象的类型有对应的 TypeHandler,则使用 TypeHandler 进行处理if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));} else {// 否则,逐个处理参数映射for (ParameterMapping param : params) {// 获取参数的属性名String propertyName = param.getProperty();MetaObject metaObject = configuration.newMetaObject(parameterObject);// 检查对象中是否存在该属性的 getter 方法,如果存在就取出来进行替换if (metaObject.hasGetter(propertyName)) {Object obj = metaObject.getValue(propertyName);sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));// 检查 BoundSql 对象中是否存在附加参数} else if (boundSql.hasAdditionalParameter(propertyName)) {Object obj = boundSql.getAdditionalParameter(propertyName);sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));} else {// SQL匹配不上,带上“缺失”方便找问题sql = sql.replaceFirst("\\?", "缺失");}}}}return sql;}private static String getParameterValue(Object object) {String value = "";if (object instanceof String) {value = "'" + object + "'";} else if (object instanceof Date) {DateFormat format = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);value = "'" + format.format((Date) object) + "'";} else if (!ObjectUtils.isEmpty(object)) {value = object.toString();}return value;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}}

直接贴的代码,其实就是在sql执行完毕之后,根据sql的template和sql参数进行?的替换。

这里不分析代码,希望能亲自debug看一下。

配置类

这里的配置我都写在了mybatis-plus的配置代码里边。

@Configuration
@MapperScan(value = "main.java.shigen.demo.dao")
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}@Beanpublic ConfigurationCustomizer configurationCustomizer() {return configuration -> {configuration.addInterceptor(new SqlInterceptor());};}
}

以上就是核心的代码了,实测遇到的问题有一个:

  • 分页查询的时候,无法显示limit 0,10这个sql后缀

希望有时间的时候能够再次优化一下。同时,也没有经过实际的项目测试,只是简单的demo测试。仅具有参考价值,无法保证实际的应用。

后记

在GPT上我也是尝试提问了一下,发现在GPT3.5模型上,没有给出满意的答复,反而是GPT4.0给出了接近我上述代码的答案。最近也在学习AI相关的课程,其中最重要的就是如何提问,也就是pormpt。

插一点一点关于AI的思考:AI其实完全可以替代人类,但是不能替代人类的想象力。所以甭管在复杂的设计、再空前绝后的设计,拥有想象力+提问的能力,都可以被AI很好的解答。

与shigen一起,每天不一样!

相关文章:

mybatis-plus使用拦截器实现sql完整打印

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 在使用mybatis-plus&#xff08;mybatis&#xff09;的时候&#xff0c;往往需要…...

GPT-4并非世界模型,LeCun双手赞同!ACL力证LLM无法模拟真实世界

一直以来&#xff0c;支持LLM的观点之一是模型可以集成海量事实知识&#xff0c;作为通往「世界模拟器」的基础。虽然也有不少反对意见&#xff0c;但缺乏实证依据。那么&#xff0c;LLM能否作为世界模拟器&#xff1f; 最近&#xff0c;亚利桑那大学、微软、霍普金斯大学等机构…...

第 6 章: Spring 中的 JDBC

JDBC 的全称是 Java Database Connectivity&#xff0c;是一套面向关系型数据库的规范。虽然数据库各有不同&#xff0c;但这些数据库都提供了基于 JDBC 规范实现的 JDBC 驱动。开发者只需要面向 JDBC 接口编程&#xff0c;就能在很大程度上规避数据库差异带来的问题。Java 应用…...

[C++ STL] vector 详解

标题&#xff1a;[C STL] vector 详解 水墨不写bug 目录 一、背景 二、vector简介 三、vector的接口介绍 &#xff08;1&#xff09;默认成员函数接口 i&#xff0c;构造函数&#xff08;constructor&#xff09; ii&#xff0c;析构函数&#xff08;destructor&#xff0…...

PHP简约轻型聊天室留言源码

无名轻聊是一款phptxt的轻型聊天室。 无名轻聊特点&#xff1a; 自适应电脑/手机 数据使用txt存放&#xff0c;默认显示近50条聊天记录 采用jqueryajax轮询方式&#xff0c;适合小型聊天环境。 访问地址加?zhi进入管理模式&#xff0c;发送 clear 清空聊天记录。 修改在…...

代码随想录算法训练营day23|669.修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

669.修剪二叉搜索树 这道题目需要考虑当前节点是否在[low,high]之间&#xff0c; 因为是平衡二叉树&#xff0c; 所以当当前节点值小于low时&#xff0c;那么其左节点肯定更小&#xff0c;因此删除该节点的方式是给root节点返回其右节点的递归&#xff0c;注意&#xff1a;这里…...

实时通信websocket和sse

microsoft/fetch-event-source是一个JavaScript库&#xff0c;用于处理服务器发送的事件&#xff08;Server-Sent Events&#xff0c;简称SSE&#xff09;。它提供了一个简单易用的API&#xff0c;使得客户端可以与服务器进行实时通信。这个库主要用于浏览器环境 安装依赖npm i…...

(超详细)基于动态顺序表实现简单的通讯录项目

前言&#xff1a; 我们在上一章节用c语言实现了线性表中的的动态顺序表&#xff0c;那么顺序表就只是顺序表吗&#xff1f;当然不是&#xff0c;使用顺序表结构可以实现很多项目&#xff0c;许多项目的数据结构都会用到顺序表&#xff0c;本章节我们就要使用顺序表实现一个简易…...

修改SubVI的LabVIEW默认搜索路径

在启动顶级VI后&#xff0c;LabVIEW可能会遇到找不到subVI的情况。这通常是由于subVI的路径发生了变化或没有被正确配置。 LabVIEW默认搜索路径 默认情况下&#xff0c;LabVIEW会按以下顺序搜索文件位置&#xff08;*表示LabVIEW将搜索子目录&#xff09;&#xff1a; <t…...

基于python深度学习的CNN图像识别鲜花-含数据集+pyqt界面

代码下载&#xff1a; https://download.csdn.net/download/qq_34904125/89383615 本代码是基于python pytorch环境安装的。 下载本代码后&#xff0c;有个requirement.txt文本&#xff0c;里面介绍了如何安装环境&#xff0c;环境需要自行配置。 或可直接参考下面博文进行…...

第九站:Java黑——安全编码的坚固防线(第②篇)

4. 验证和过滤输入数据示例&#xff1a;使用Apache Commons Lang 对输入数据进行验证和过滤是防止多种安全漏洞的关键步骤&#xff0c;包括但不限于SQL注入和命令注入。Apache Commons Lang库提供了一些实用方法来帮助进行字符串操作和验证。以下是一个简单的示例&#xff0c;…...

如何优雅的删除正式环境中的大表

引起 MySQL 数据库性能抖动的原因有很多,比如大事务、定时批量查询等,而这些原因我们一般都会注意到。但是,有一个引起性能抖动的原因却经常被我们忽视,那就是在生产环境删除无用的大表,即 DROP TABLE。 一、为什么要 DROP TABLE? 生产环境中,为什么要 DROP TABLE?相…...

Vulnhub-DC-1,7

靶机IP:192.168.20.141 kaliIP:192.168.20.128 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) 前言 1和7都是Drupal的网站&#xff0c;只写了7&#xff0c;包含1的知识点 信息收集 用nmap扫描端口及版本号 进入主页查看作者给的提示&#xff0c;不是暴力破解的…...

使用MySQL全文索引实现高效搜索功能

MySQL全文索引是MySQL提供的一种高效的搜索功能&#xff0c;可以快速地搜索文本内容。全文索引可以用于搜索大量文本数据&#xff0c;通常应用在文章、博客、论坛等需要搜索的场景中。 什么是MySQL全文索引 MySQL全文索引是一种用于快速搜索文本内容的索引技术。它可以在存储和…...

数据结构学习笔记-图

1.图的存储 &#xff08;1&#xff09;邻接矩阵法 #define MaxVertexNum 100 //顶点数目的最大值 typedef struct{char Vex[MaxVertexNum]; //顶点表int Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵表&#xff0c;边表int vexnum,arcnum; //图的当前顶点数和边…...

【归并排序】| 详解归并排序核心代码之合并两个有序数组 力扣88

&#x1f397;️ 主页&#xff1a;小夜时雨 &#x1f397;️专栏&#xff1a;动态规划 &#x1f397;️如何活着&#xff0c;是我找寻的方向 目录 1. 题目解析2. 代码 1. 题目解析 题目链接: https://leetcode.cn/problems/merge-sorted-array/description/ 本道题是归并排序的…...

51单片机STC89C52RC——2.3 两个独立按键模拟控制LED流水灯方向

目的 按下K1键LED流水向左移动 按下K2键LED流水向右移动 一&#xff0c;STC单片机模块 二&#xff0c;独立按键 2.1 独立按键位置 2.2 独立按键电路图 这里要注意一个设计的bug P3_1 引脚对应是K1 P3_0 引脚对应是K2 要实现按一下点亮、再按一下熄灭&#xff0c;我们就需…...

Neo4j连接

终端输入&#xff1a; neo4j console 浏览器访问&#xff1a;http://localhost:7474/ 输入用户名和密码&#xff1a;neo4j&#xff0c; 梦想密码&#xff08;首次neo4j&#xff09; 代码连接用新的服务器地址&#xff1a; g Graph(neo4j://localhost:7687, auth(neo4j, ))…...

List 列表

文章目录 一、什么是 List 列表1.1 创建 List 列表的方式1.2 列表的新增函数方法1.3 列表的删除函数方法1.4 修改列表数据的方法1.5 列表的查询函数方法1.6 列表的排序和反序1.7 列表的复制 一、什么是 List 列表 List 列表&#xff1a;该数据类型定义的变量可以理解为是一个数…...

nginx ws长连接配置

nginx ws长连接配置 http根节点下配上 map $http_upgrade $connection_upgrade {default upgrade; close;}如下&#xff1a; server服务节点下&#xff0c;后端接口的代理配置 proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connec…...

Windows下访问wsl的数据

Windows下访问wsl的数据 有些人感受到的是雨&#xff0c;而很多人感受到的只有淋湿。 Windows下的wsl说实话还是挺不错的&#xff0c;对于开发而言&#xff0c;效果相当的可以。 比如在某个文件夹&#xff0c;Windows编辑好代码后&#xff0c;直接右键打开wsl&#xff0c;就可…...

机器学习笔记 - 用于3D数据分类、分割的Point Net简述

一、简述 在本文中,我们将了解Point Net,目前,处理图像数据的方法有很多。从传统的计算机视觉方法到使用卷积神经网络到Transformer方法,几乎任何 2D 图像应用都会有某种现有的方法。然而,当涉及到 3D 数据时,现成的工具和方法并不那么丰富。3D 空间中一个工具就是Point …...

vscode 连接 GitHub

目录 vscode连接github一、解决 github 登录问题二、通过 SSH 连接 github1、只有一个 git 账号2、切换 git 账号3、在两个账号之间切换 vscode 连接 gitee一、通过 HTTPS 连接二、通过 SSH 连接 vscode连接github 在 vscode 中首次使用 git push 命令时会要求输入 github 账户…...

集合java

1.集合 ArrayList 集合和数组的优势对比&#xff1a; 长度可变 添加数据的时候不需要考虑索引&#xff0c;默认将数据添加到末尾 package com.itheima;import java.util.ArrayList;/*public boolean add(要添加的元素) | 将指定的元素追加到此集合的末尾 | | p…...

智能体(Agent)实战——从gpts到auto gen

一.GPTs 智能体以大模型作为大脑&#xff0c;同时配备技能&#xff0c;使其能够完成具体的任务。同时&#xff0c;为了应用于垂直领域&#xff0c;我们需要为大模型定义一个角色&#xff0c;并构建知识库。最后&#xff0c;定义完整的流程&#xff0c;使其完成整个任务。以组会…...

PyTorch 张量数据类型

【数据类型】Python 与 PyTorch 常见数据类型对应&#xff1a; 用 a.type() 获取数据类型&#xff0c;用 isinstance(a, 目标类型) 进行类型合法化检测 >>> import torch >>> a torch.randn(2,3) >>> a tensor([[-1.7818, -0.2472, -2.0684],[ 0.…...

奇思妙想-可以通过图片闻见味道的设计

奇思妙想-可以通过图片闻见味道的设计 偷闲半日享清闲&#xff0c;炭火烧烤乐无边。肉串飘香引客至&#xff0c;笑语欢声绕云间。人生难得几回醉&#xff0c;且把烦恼抛九天。今宵共饮开怀酒&#xff0c;改日再战新篇章。周四的傍晚&#xff0c;难得的闲暇时光让我与几位挚友相…...

装饰者模式(设计模式)

装饰模式就是对一个类进行装饰&#xff0c;增强其方法行为&#xff0c;在装饰模式中&#xff0c;作为原来的这个类使用者还不应该感受到装饰前与装饰后有什么不同&#xff0c;否则就破坏了原有类的结构了&#xff0c;所以装饰器模式要做到对被装饰类的使用者透明&#xff0c;这…...

ADB调试命令大全

目录 前言命令大全1.显示当前运行的全部模拟器&#xff1a;adb devices2.启动ADB: adb start-server3.停止ADB: adb kill-server4.安装应用程序&#xff1a; adb install -r [apk文件]5.卸载应用程序&#xff1a; adb uninstall [packagename]6.将手机设备中的文件copy到本地计…...

查看npm版本异常,更新nvm版本解决问题

首先说说遇见的问题&#xff0c;基本上把nvm&#xff0c;npm的坑都排了一遍 nvm版本导致npm install报错 Unexpected token ‘.‘install和查看node版本都正确&#xff0c;结果查看npm版本时候报错 首先就是降低node版本… 可以说基本没用&#xff0c;如果要降低版本的话&…...

怎么做电商平台网站/google浏览器官网

只讲使用mdev创建设备文件。。。。。1、mdev的用法可以参考busybox-1.9.2/docs/mdev.txt,它的两个用途&#xff1a;初始化/dev目录、动态更新。以下为摘取部分关于mdev的命令&#xff1a;Heres a typical code snippet from the init script:[1] mount -t sysfs sysfs /sys[2] …...

专做衬衣的网站/今日新闻十大头条内容

原题链接 hdu1878 大致题意: 欧拉回路是指不令笔离开纸面&#xff0c;可画过图中每条边仅一次&#xff0c;且可以回到起点的一条回路。现给定一个无向图&#xff0c;问是否存在欧拉回路&#xff1f; 思路: 无向图存在欧拉回路的条件&#xff1a;1、图是连通的 2、所有点的度数…...

要维护公司的网站该怎么做/免费注册网址

2019独角兽企业重金招聘Python工程师标准>>> 今天决定看看开源中国安卓版app&#xff0c;并试着重构一下。好的进入主题。 创建MainActivity public class MainActivity extends ActionBarActivity implementsNavigationDrawerFragment.NavigationDrawerCallbacks,O…...

可以做网站的域名后缀/电商网站建设开发

所有的php文件放到同一个目录下 ../Trie/ ├── CharMap.php ├── Map.php ├── StdMap.php ├── Trie.php ├── TrieNode.php ├── index.php ├── test.php~ └── words.txt * TrieNode.php <?php /*** Class TrieNode* 字典树是利用字符串的公…...

英山县住房和城乡建设局网站/浏览器下载安装2023版本

Liunx要查找某个文件&#xff0c;但不知道放在哪里&#xff0c;可以通过下面命令来查找&#xff1a;&#xff08;1&#xff09;which 查看可执行文件的位置&#xff08;2&#xff09;whereis 查看文件的位置&#xff08;3&#xff09;locate 配合数据库查看文件位置&#xff08…...

互联网网站开发服务合同/友情链接交换统计表

1. 问题描述&#xff1a; 给定一个长度为 n 的 01 字符串。请你判断&#xff0c;其中是否存在子串 1111111 或 0000000。 输入格式 一行&#xff0c;一个 01 字符串。 输出格式 如果存在子串 1111111 或 0000000&#xff0c;则输出 YES&#xff0c;否则输出 NO。 数据范围…...