MyBatis 参数重复打印的bug

现象

最近有个需求,需要在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.mzph.cn/news/785745.shtml

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

相关文章

C++之调用Python

1、配置头文件 Python安装目录下的include目录加入头文件目录。Visual Studio2022中操作路径是:属性–> C/C -> 常规-> 附加包含目录 C:\Users \AppData\Local\Programs\Python\Python39\include 2、配置lib库目录 要将Python39.lib加入编译链接。Visua…

neo4j使用详解(七、cypher数学函数语法——最全参考)

Neo4j系列导航: neo4j及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 5.数学函数 5.1.数值函数 数学函数仅对数字表达式进行运算,如果对任何其他值使用,将返回错误 abs()&#xf…

Nginx 基础

文章目录 Nginx概念安装下载上传安装包执行准备条件指定安装位置编译和安装启动服务创建启动脚本 linux文件目录nginx运行原理nginx配置域名概念和原理域名配置 Nginx 概念 Nginx 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是…

【Java八股面试系列】Arraylist和HashMap的底层原理

文章目录 ArrayList源码总:构造方法扩容机制remove HashMap总:构造方法细节问题putVal()方法resize()方法Hash值 HashMap常见问题 ConcurrentHashMap总:putVal()方法自己的测试 为什么重写HashCode和equals ArrayList源码 总: *…

3.28号arm

can总线相关理论 1. 概念 控制器局域网(Controller Area Network,CAN),其特点是可拓展性好,可承受大量数据的高速通信,高度稳定可靠,因此常应用于汽车电子领域、工业自动化、医疗设备等高要求…

Java JSON字符串相关问题

一、依赖包 <!--json包--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.15</version></dependency> 二、举例 1.实体对象转Json字符串 1.1 代码实现 Dog.java: pack…

python_web1(前端开发之HTML、CSS、Bootstap、Javascript、JQuery)

文章目录 一、Flask网页开发1.1创建一个名为web1.py的python文件1.2 templates目录创建文件index.html 二、html标签2.1 编码2.2title < head >2.3 标题< h>2.4 div和span2.5超链接1.在index.xml文件中补充。2.修改web1.py文件3.添加get_self.html4.效果 2.6图片1.…

Java 堆外内存及调优

文章目录 直接内存简介为什么DirectByteBuffer可以优化 IO 性能 直接内存的分配直接内存的回收直接内存跟踪与诊断 直接内存简介 直接内存(Direct Memory) 并不是虚拟机运行时数据区的一部分&#xff0c;并非Java虚拟机规范中定义的内存区域。但是这部分内存的频繁使用&#x…

【LeetCode】三月题解

文章目录 [2369. 检查数组是否存在有效划分](https://leetcode.cn/problems/check-if-there-is-a-valid-partition-for-the-array/)思路&#xff1a;代码&#xff1a; [1976. 到达目的地的方案数](https://leetcode.cn/problems/number-of-ways-to-arrive-at-destination/) 思路…

C++教学——从入门到精通 5.单精度实数float

众所周知&#xff0c;三角形的面积公式是(底*高)/2 那就来做个三角形面积计算器吧 到吗如下 #include"bits/stdc.h" using namespace std; int main(){int a,b;cin>>a>>b;cout<<(a*b)/2; } 这不对呀&#xff0c;明明是7.5而他却是7&#xff0c;…

让IIS支持.NET Web Api PUT和DELETE请求

前言 有很长一段时间没有使用过IIS来托管应用了&#xff0c;今天用IIS来托管一个比较老的.NET Fx4.6的项目。发布到线上后居然一直调用不同本地却一直是正常的&#xff0c;关键是POST和GET请求都是正常的&#xff0c;只有PUT和DELETE请求是有问题的。经过一番思考忽然想起来了I…

YOLOv9改进策略 :主干优化 | 极简的神经网络VanillaBlock 实现涨点 |华为诺亚 VanillaNet

💡💡💡本文改进内容: VanillaNet,是一种设计优雅的神经网络架构, 通过避免高深度、shortcuts和自注意力等复杂操作,VanillaNet 简洁明了但功能强大。 💡💡💡引入VanillaBlock GFLOPs从原始的238.9降低至 165.0 ,保持轻量级的同时在多个数据集验证能够高效涨点…

每日学习笔记:C++ STL算法分类

非更易型 更易型 移除型 变序型 排序型 已排序区间算法 数值型算法

【滑动窗口】Leetcode 将 x 减到 0 的最小操作数

题目解析 1658. 将 x 减到 0 的最小操作数 算法讲解 这道题按照题目要求的话会变得很难&#xff0c;因为不仅需要考虑数字减到0&#xff0c;还需要考虑最小的操作数。正难则反&#xff0c;按照这个思路&#xff0c;我们来解析题目 这道题本质上无非就是在左边寻找一段区间&a…

HCIP第三次作业(综合)

一、实验要求 二、实验步骤 1、配置IP地址部分 PC1&#xff1a; PC2&#xff1a; PC3&#xff1a; PC4&#xff1a; R1&#xff1a; R2&#xff1a; R3&#xff1a; R4&#xff1a; R5&#xff1a; 环回&#xff1a; 2.通过配置缺省路由让公网互通 [R1]ip route-static 0.0.…

代码随想录第27天| 39. 组合总和

39. 组合总和 39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 带你学透回溯算法-组合总和&#xff08;对应「leetcode」力扣题目&#xff1a;39.组合总和&#xff09;| 回溯法精讲&#xff01;_哔哩哔哩_bilibili 给你一个 无重复元…

思考:开启MMU瞬间可能出现的多种问题以及多种解决方案

快速链接: 【精选】ARMv8/ARMv9架构入门到精通-[目录] &#x1f448;&#x1f448;&#x1f448; (说明本文的介绍都是基于armv8-aarch64或armv9硬件架构) 在mmu未开启阶段&#xff0c;PC操作的都是物理地址执行程序&#xff0c;这样看起来一切正常&#xff0c;没啥问题。 例如…

Windows Server 2022 使用ApacheDS用户远程桌面登录服务器

Windows Server 2022 使用ApacheDS用户远程桌面登录服务器 1、接上篇 Windows Server 2022 使用ApacheDS用户认证 使用Administrator用户远程登录192.168.1.100windows server&#xff0c;打开pGina软件 2、输入刚刚在ApacheDS中的新添加的用户测试一下&#xff0c;会自动添加…

如何在极狐GitLab 配置 邮件功能

本文作者&#xff1a;徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了在极狐GitLab 用户…

【带你了解下前端开发语言有那些】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…