mybatis中foreach重复使用一个对象的问题

2023-12-09 00:08

本文主要是介绍mybatis中foreach重复使用一个对象的问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

需求:在一个非常夸张的SQL中,出现了一个需求点,在这个SQL中需要将同一个collection对象进行多次循环输出。

画外音:该SQL不能重构,改了恐怕会错的更多,只能采取打补丁的方式,等待下一个接收这个SQL的人员,哈哈。

出现的问题:

本人采取foreach的方式来遍历该集合对象,整个SQL看起来像这样

select id,name from A
where
name in
<foreach item="item" collection="myList" separator="," open="(" close=")" index="">#{item}
</foreach>unionselect id,name from B
where
name in
<foreach item="item" collection="myList" separator="," open="(" close=")" index="">#{item}
</foreach>

对于第一次出现的循环,打印出的SQL结果是正常的,但是第二次出现的循环却找不到内容,感觉这个对象好像不存在了。

排查方法:

1、当然是在网上找有没有类似的问题撒,参考了这个文档https://blog.csdn.net/weixin_30549657/article/details/96281457,修改了item的名称,发现还是木有效果

2、从源码角度来找问题源头,根据网上大神们的源码解读,找到了ExpressionEvaluator这个类,贴出的部分源码就是对集合对象进行SQL动态替换的入口

public Iterable<?> evaluateIterable(String expression, Object parameterObject) {Object value = OgnlCache.getValue(expression, parameterObject);if (value == null) throw new BuilderException("The expression '" + expression + "' evaluated to a null value.");if (value instanceof Iterable) return (Iterable<?>) value;if (value.getClass().isArray()) {// the array may be primitive, so Arrays.asList() may throw// a ClassCastException (issue 209).  Do the work manually// Curse primitives! :) (JGB)int size = Array.getLength(value);List<Object> answer = new ArrayList<Object>();for (int i = 0; i < size; i++) {Object o = Array.get(value, i);answer.add(o);}return answer;}if (value instanceof Map) {return ((Map) value).entrySet();}

设置了断点,跳到了ForEachSqlNode这个类,其中贴出的部分源码,就是动态替换集合的内容了,其中int uniqueNumber = context.getUniqueNumber();就是可以解释同一个集合对象,多次使用不生效的原因。因为同一个对象,一旦遍历之后它的偏移量不会重置,类似于只执行一次。 

public boolean apply(DynamicContext context) {Map<String, Object> bindings = context.getBindings();final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings);boolean first = true;applyOpen(context);int i = 0;for (Object o : iterable) {DynamicContext oldContext = context;if (first) {context = new PrefixedContext(context, "");} else {if (separator != null) {context = new PrefixedContext(context, separator);} else {context = new PrefixedContext(context, "");}}int uniqueNumber = context.getUniqueNumber();if (o instanceof Map.Entry) { // Issue #709 @SuppressWarnings("unchecked") Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o;applyIndex(context, mapEntry.getKey(), uniqueNumber);applyItem(context, mapEntry.getValue(), uniqueNumber);} else {applyIndex(context, i, uniqueNumber);applyItem(context, o, uniqueNumber);}contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber));if (first) first = !((PrefixedContext) context).isPrefixApplied();context = oldContext;i++;}applyClose(context);return true;}

解决方法:

根据阅读源码得出的这个结论,解决思路也很清晰了,就是再定义一个变量就可以了,变量的内容跟第一个变量内容保持一致即可。just do it。

吭哧吭哧,验证正确,打完收工。最后的SQL类似于这样,myList跟myList2内容一样

select id,name from A
where
name in
<foreach item="item" collection="myList" separator="," open="(" close=")" index="">#{item}
</foreach>unionselect id,name from B
where
name in
<foreach item="item2" collection="myList2" separator="," open="(" close=")" index="">#{item2}
</foreach>

 

 

 

 

这篇关于mybatis中foreach重复使用一个对象的问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/471790

相关文章

MySQL安装时initializing database失败的问题解决

《MySQL安装时initializingdatabase失败的问题解决》本文主要介绍了MySQL安装时initializingdatabase失败的问题解决,文中通过图文介绍的非常详细,对大家的学... 目录问题页面:解决方法:问题页面:解决方法:1.勾选红框中的选项:2.将下图红框中全部改为英

Nginx启动失败:端口80被占用问题的解决方案

《Nginx启动失败:端口80被占用问题的解决方案》在Linux服务器上部署Nginx时,可能会遇到Nginx启动失败的情况,尤其是错误提示bind()to0.0.0.0:80failed,这种问题通... 目录引言问题描述问题分析解决方案1. 检查占用端口 80 的进程使用 netstat 命令使用 ss

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

mybatis和mybatis-plus设置值为null不起作用问题及解决

《mybatis和mybatis-plus设置值为null不起作用问题及解决》Mybatis-Plus的FieldStrategy主要用于控制新增、更新和查询时对空值的处理策略,通过配置不同的策略类型... 目录MyBATis-plusFieldStrategy作用FieldStrategy类型每种策略的作

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

Python Jupyter Notebook导包报错问题及解决

《PythonJupyterNotebook导包报错问题及解决》在conda环境中安装包后,JupyterNotebook导入时出现ImportError,可能是由于包版本不对应或版本太高,解决方... 目录问题解决方法重新安装Jupyter NoteBook 更改Kernel总结问题在conda上安装了

pip install jupyterlab失败的原因问题及探索

《pipinstalljupyterlab失败的原因问题及探索》在学习Yolo模型时,尝试安装JupyterLab但遇到错误,错误提示缺少Rust和Cargo编译环境,因为pywinpty包需要它... 目录背景问题解决方案总结背景最近在学习Yolo模型,然后其中要下载jupyter(有点LSVmu像一个

解决jupyterLab打开后出现Config option `template_path`not recognized by `ExporterCollapsibleHeadings`问题

《解决jupyterLab打开后出现Configoption`template_path`notrecognizedby`ExporterCollapsibleHeadings`问题》在Ju... 目录jupyterLab打开后出现“templandroidate_path”相关问题这是 tensorflo

如何解决Pycharm编辑内容时有光标的问题

《如何解决Pycharm编辑内容时有光标的问题》文章介绍了如何在PyCharm中配置VimEmulator插件,包括检查插件是否已安装、下载插件以及安装IdeaVim插件的步骤... 目录Pycharm编辑内容时有光标1.如果Vim Emulator前面有对勾2.www.chinasem.cn如果tools工

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动