MyBatis 参数重复打印的bug

news2024/10/7 2:28:32

现象

最近有个需求,需要在mybatis对数据库进行写入操作的时候,根据条件对对象中的某个值进行置空,然后再进行写入,这样数据库中的值就会为空了。

根据网上查看的资料,选择在 StatementHandler 类执行 update 的时候进行对参数的拦截,

修改参数完毕后,再调用 statementHandler.getParameterHandler().setParameters 方法将修改后的值重新 set 进去,

此时出现了问题,发现mybatis在控制台log中打印出来的参数竟然多了一份,下面是临摹的情况,没有实际用update ,而是拦截的query方法,简化了一下逻辑:

这里我调用了3次 setParameters ,加上mybatis本身的一次,输出了4个 Parameters

在这里插入图片描述

实际上我的xml中只接收了一个参数,并且虽然log多打印了一些参数,实际对我的结果并无影响。

在这里插入图片描述

为什么会发生这个问题

问题就出在 StatementHandler.getParameterHandler().setParameters 这个方法上,这里是对本次数据库操作的参数进行赋值,

这过程中有一个 typeHandler.setParameter(ps, i + 1, value, jdbcType) 操作,
在这里插入图片描述

这里会根据相应的类型处理器,进行赋值,这个过程如下,typeHandler进行赋值操作,

这时候会调用 jdbcPreparedStatement.setXxx 方法,但是 mybatis对 PreparedStatement 做了一个拦截,

在这里插入图片描述

这个拦截就是日志记录类 PreparedStatementLogger ,当调用setXxx方法时,

在这里插入图片描述
Logger类会调用 setColumn 方法,这个setColumn方法就是记录本次入参情况,

在这里插入图片描述

最终调用 PreparedStatement.executeXXX方法时,本次代理将会根据条件决定要不要打印出参数等log,
在这里插入图片描述
而这个参数log,就是前面我们提到的setColumn方法中存入的 columnValues 属性,

在这里插入图片描述
可以看到,这个属性使用的是 ArrayList ,这也就说明了为什么会重复打印参数log出来,这意味着当你多次调用 StatementHandler.getParameterHandler().setParameters 这个方法时,columnValues 不会清除之前记录的参数,并且继续保存你这次重新set的参数进来。

在这里插入图片描述

如何解决

如果mybatis不使用ArrayList存值是否就可以避免这个问题,并且columnNamescolumnValues这两个值仅仅在打印log的时候使用,并没有在其他地方有使用到,

columnMap 刚好起到了,就算多次调用StatementHandler.getParameterHandler().setParameters方法,但是因为Map有过滤重复key的作用,然后使用columnMap中记录的值就可以防止参数重复打印的问题,

在这里插入图片描述

在这里插入图片描述
于是我给mybatis提了一个pr,借此来修复这个问题, pr链接:https://github.com/mybatis/mybatis-3/pull/3110

我的思路很简单,去除columnNamescolumnValues,仅保留columnMap,并且将columnMap改为LinkedHashMap类型,以此保证参数的顺序,经过测试这样做并没有发现什么问题,且保证了参数的正确输出。
在这里插入图片描述
不过很遗憾我的pr没有被合并,被关闭了,对方给的理由是不保证在拦截器中做出的一些操作对mybatis的运行产生一些的副作用,且给出友好提示,是否有其他的方式来解决我的需求问题。

重新判断问题

我们重新思考一下这个问题,问题出现在StatementHandler.getParameterHandler().setParameters 这里,我对参数重新赋值会导致这个问题,

那么我为何要重新赋值?有没有办法不调用这个StatementHandler.getParameterHandler().setParameters 方法?

我需要重新赋值的原因是因为在拦截StatementHandlerupdatequery方法时,mybatis自身已经调用过setParameters方法,

此时如果我不重新调用一下,单纯的修改parameterObject 自身,那么PreparedStatement.executeXXX设置的参数其实还是上次的,并不会因为我修改了parameterObject 而变化,

所以根据提示,我们有没有办法在mybatis自身执行setParameters前进行对parameterObject 的修改,这样我们在执行过程中,由mybatis来做这个赋值的事情,log就只会打印一次了。

最终解决

那么mybatis是何时自己进行setParameters的?答案在StatementHandler.parameterize中,

是的,它会在update或者query方法执行前,对参数进行处理,所以我们应当拦截这一步的操作,在这一步对参数进行处理,

在这里插入图片描述
修改后的拦截器,可以看到这里我们对StatementHandler.parameterize方法进行拦截处理,并且修改参数,此时log中输出的就是我们最后一次修改的参数。
在这里插入图片描述

当然mybatis还提供了其他的拦截点,例如不拦截StatementHandler类,我们直接到源头ParameterHandler.setParameters,拦截设置参数方法,

在这里,我们修改他的参数也是可以的,如下图:

在这里插入图片描述

最终结论

可以看到,我们只要不在拦截器中调用setParameters方法,就不会触发log的重复打印,因为mybatis的log记录类,使用ArrayList记录每次的setXX入参, 因此选好时机做相应的处理,就不会出现问题,在合适的拦截点做相应的事情,

MyBatis的参数记录可能也没有考虑过重复调用的问题,或者也许有其他的考量,总之我们了解这个问题的原因,并且做相应的规避即可。

演示与复现问题的demo都在:https://github.com/qiaomengnan16/mybatis-log-bug,欢迎指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1560997.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虚拟内存到物理地址的映射,是CPU做的,还是操作系统做的?

虚拟地址到物理地址的转换,是CPU实现得,具体来说,就是CPU的内存管理单元 (Memory Management Unit, MMU)实现的。 为了加速地址翻译的 过程,现代CPU都引入了转址旁路缓存(Translation Loopasid…

HarmonyOS 应用开发之I/O密集型任务开发指导 (TaskPool)

使用异步并发可以解决单次I/O任务阻塞的问题,但是如果遇到I/O密集型任务,同样会阻塞线程中其它任务的执行,这时需要使用多线程并发能力来进行解决。 I/O密集型任务的性能重点通常不在于CPU的处理能力,而在于I/O操作的速度和效率。…

TDK超高压陶瓷电容的国产替代---赫威斯电容HVC Capacitor

螺栓型高压陶瓷电容,英文称为Doorknob Capacitor(美式门拧手式电容)或者High Voltage Screw Terminal Ceramic Capacitor(高压螺栓端子陶瓷电容), 著名的日本TDK公司(东电化电子)称其为“超高压陶瓷电容器”(Ultra High Voltage Ceramic capacitors)。 自2018年秋日本电子业巨头…

分治——归并排序算法

例题一 解法(归并排序): 算法思路: 归并排序的流程充分的体现了「分⽽治之」的思想,⼤体过程分为两步: ◦ 分:将数组⼀分为⼆为两部分,⼀直分解到数组的⻓度为 1 ,使…

STM32——超声测距HC_SR04记录

一、HC_SR04简述 HC-SR04超声波测距模块可提供 2cm-400cm的非接触式距离感测功能,测距精度可达高到 3mm;模块包括超声波发射器、接收器与控制电路。 基本工作原理: (1)采用IO 口TRIG 触发测距,给最少10us 的高电平信呈。 (2)模块…

差点引爆全球的核弹,深度分析XZ-Utils供应链后门投毒事件

处心积虑的投毒者蛰伏三年多,精心选择对象,通过复杂的攻击手法、专业的技战术,一步步支起一张大网,企图掌控全球主流linux发行版,一旦成功他将可以随意侵入全球绝大多数的服务器,这将是足以引爆全球的核弹危…

java高级面试题整理 - 2024

Java 创建对象有几种方式 在Java中,有以下几种常见的方式来创建对象: 使用new关键字:这是最常见的创建对象的方式。通过调用类的构造函数,使用new关键字可以在内存中分配一个新的对象。使用反射:Java的反射机制允许在…

【漏洞复现】通天星CMSV6弱口令漏洞

免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该…

Anaconda换源和常用命令

设置Anaconda国内镜像加速下载 使用conda install python包非常便捷,但由于官方服务器位于国外,下载速度较慢。为了提升下载速度,国内清华大学提供了Anaconda的仓库镜像。 要将Anaconda设置为使用国内镜像,特别是清华镜像源&…

前后端数据交互

前后端数据交互 网页上所有的数据都是来源于后端,比如淘宝或者京东的秒杀,用户的登陆或者注册,这些都需要借助于后端来存储数据。我们前端需要做的就是把数据发送给后端,后端发送给我们的数据我们要拿到把它显示到页面上&#xff…

高斯消元、组合计数

数据结构、算法总述&#xff1a;数据结构/算法 C/C-CSDN博客 高斯消元 // a[N][N]是增广矩阵 int gauss() {int c, r;for (c 0, r 0; c < n; c ){int t r;for (int i r; i < n; i ) // 找到绝对值最大的行if (fabs(a[i][c]) > fabs(a[t][c]))t i;if (fabs(a…

数据可视化-ECharts Html项目实战(9)

在之前的文章中&#xff0c;我们学习了如何在ECharts中编写气泡图&#xff0c;词云图。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 数据可视化-ECharts Ht…

git仓库太大只下载单个文件或文件夹

有没有这样的苦恼&#xff1a;仓库太大&#xff0c;只想下载其中某些文件(夹)&#xff1f; 一招解决&#xff1a;bash down_folder_from_git.sh 运行前&#xff0c;先修改开头三个变量 原理: 稀疏检出 让工作树仅包含自定义的文件 #!/usr/bin/bash addrhttps://github.com/fac…

千行百业加速鸿蒙化,5分钟搞懂鸿蒙开发有必要学吗?

鸿蒙系统在全球范围内取得了显著进展&#xff0c;其生态设备数量已经突破8亿大关。更令人振奋的是&#xff0c;已有超过200家领先的互联网应用公司纷纷加速鸿蒙原生开发的步伐。 这其中包括了我们日常生活中耳熟能详的淘宝、支付宝、美团、京东、高德、小红书等热门应用&#…

AI技术创业:挖掘行业解决方案、智能产品服务及教育培训的无限机遇

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

AI大模型和BI如何结合?

人工智能大模型和商业智能&#xff08;Business Intelligence&#xff0c;BI&#xff09;可以结合起来&#xff0c;可以涵盖以下几个方面&#xff1a; 数据分析和预测&#xff1a;人工智能大模型可以通过深度学习和大数据处理技术&#xff0c;对大规模的数据进行分析和预测。通…

org.junit.runners.model.InvalidTestClassError:1. No runnable methods

你们好&#xff0c;我是金金金。 场景 很简单的一个测试方法 我的boot版本&#xff1a;2.7.18 依赖 报错信息 排查 看报错信息提示无效的测试类&#xff0c;没有可运行的方法 看了下依赖信息&#xff0c;引入spring-boot-starter-test依赖也自动的引入了juni5依赖&#xff0…

latex表格前后都有多行合并

最终实现的效果图如下&#xff1a; 这个表格的特点是前面有5行合并&#xff0c;后边也有5行合并&#xff0c;眉头有2行合并。 当然要引入对应的包 \usepackage{multirow} 其实现代码如下&#xff1a; \begin{table*}[!t]\caption{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. \label{X…

论文笔记✍GS3D- An Efficient 3D Object Detection Framework for Autonomous Driving

论文笔记✍GS3D: An Efficient 3D Object Detection Framework for Autonomous Driving &#x1f4dc; Abstract &#x1f528; 主流做法限制 &#xff1a; 我们在自动驾驶场景中提出了一种基于单个 RGB 图像的高效 3D 物体检测框架。我们的工作重点是提取 2D 图像中的底层 3…

八、组合数据类型(列表、元组、集合、字典)

序列&#xff1a;存储多个值的连续空间&#xff0c;每个值对应一个编号————索引 包括&#xff1a;列表、元组、集合和字典 相加操作 s1"桂林山水" s2山水甲天下 print(s1s2)#直接相加得到新的字符串 print(_____________________________) print((s1s2)*5,sep&…