带你重新认识MyBatis的foreach
作者:BillySir 发布时间:2023-11-21 08:44:54
用了MyBatis的同行,应该见过foreach
,它一般是这样用的:
<select id="foreachTest" resultType="Blog">
select * from t_blog where id in
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
难记
有没有人跟我一样,觉得
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
这一串很啰嗦?每次写<foreach>
都因为记不住和怕记不牢,要查找和复制现有的来改。假如可以简化,就不至于要这样了。
一项项来分析:
foreach
这个是xml标签,少不了。collection
可以通过探测参数类型+数量,从而在大多数情况下,知道对应哪个参数而省略。index
这个就更加不用写了,幸好它真的是用时才写,不用不写。item
假如缺省值就是item
,则可能会有一些冲突,比如某个表名叫item
,会不会让人掉坑里才豁然知道缘由?想不清楚。open
难道不是通常都是"("吗?但只要不写open
,缺省值是没有,即是空串,很无语,强迫我们几乎每个foreach都要写open="("
separator
难道不是绝大多数都是","吗?缺省值也是空串,更无语close
和open
同理。
空集合问题
还有一个问题,当传入的集合是空集的时候,比如上面这个例子,某些情况计算出来的list是空集合。按道理,既然list是id的集合,空集合意味着应该select到0条记录。但结果却是报SQL语法错误。原因是生成的SQL长这样:
select * from t_blog where id in ; -- 为了明显,我补了个分号
是的,按SQL语法,in后面的小括号是不能没有内容的,小括号本身也是不能省的,抓狂。
在以前,我接触的项目有DAO层(指java interface mapper之上的一层),我会在DAO层先判断,若list是Empty,直接返回空集,否则再执行mybatis的查询。后来的一些项目,推崇简单化,没有DAO层,Service直接调用java interface mapper。把这个“判空返回空”的逻辑写在Service就显得别扭。难道MyBatis就不能优雅地解决这个问题吗?
解法
直到后来看到了这种写法:
<select id="foreachTest" resultType="Blog">
select * from t_blog
<where>
<foreach collection="list" index="index" item="item" open=" id in (" separator="," close=")">
#{item}
</foreach>
</where>
</select>
特别之处在于open="id in ("
,与一开始的id in
写在foreach外面有什么不同呢?经过实验,结论是这样的:
当list非空时,两者并无区别。
当list为空时,既不生成
open
值,也不生成close
值!
所以,当list为空时,SQL就是:select * from t_blog
,也就没有违反语法。但是查到的是全部的记录,而不是预期的“0条记录”。
改进为这样写
<select id="foreachTest" resultType="Blog">
select * from t_blog
<where>
1 = 0
<foreach collection="list" index="index" item="item" open=" or id in (" separator="," close=")">
#{item}
</foreach>
</where>
</select>
如果,还有其它的and
条件,则需要在or
的两边加上小括号。
<select id="foreachTest" resultType="Blog">
select * from t_blog
<where>
(1 = 0
<foreach collection="list" index="index" item="item" open=" or id in (" separator="," close=")">
#{item}
</foreach>
)
and ...
</where>
</select>
这样,不论list是否empty,都会生成正确语法和功能的SQL语句。有兴趣的朋友可自行推导。
优雅的解法
但我觉得上面的写法不够优雅,经过实验,找到一种我认为更优雅的写法:
<select id="foreachTest" resultType="Blog">
select * from t_blog
<where>
<foreach collection="list" index="index" item="item" open=" and id in (" close="-1)">
#{item},
</foreach>
and ...
</where>
</select>
留意:
separator
没了,缺省就是空串#{item}
后面有了个逗号(,
)close
里多了个-1
是这样的思路,我们要解决的是in
后面是空的问题,假如在list为empty时,也能“塞”入一个不存在的值,比如这里规定id不可能是-1,那么生成的sql就是where id in (-1)
,语法没错,且查不到数据。而当list非空时,生成的sql如where id in (1,2,3,-1)
,虽然有-1
,但不影响查出来的结果。只是本来3
是最后一个,不需要加逗号,由于后面固定有-1
,所以每个#{item}
后面固定要有逗号,如果逗号放在separator,则只出现在各个id之间,放在#{item}
就可以出现在每个item后了。
通过此案例,回头一看,思路一下子就打开了。open
里的写法可以很灵活,只要符合SQL语法的,理论上都可以往open
里放。separator
、close
也一样。可能作者就是因为这些灵活的用法,所以干脆让它们的缺省值是空串。这本无标准,所以不能说这样做就是对或错的。
一种简化<foreach>的设想
但如果允许我修改设计的话,我会让open
的缺省值为"("
,separator
的缺省值为", "
,close
的缺省值为")"
。因为大多数情况下它们的值就是这样。假如特殊情况下,就想要它的值为空串,可以这么写open=""
,何尝不可。这样做的好处是,通常情况下foreach会很简洁:
<foreach collection="list" item="item">
#{item}
</foreach>
例如,像文章开头分析的那样,还可以去掉collection,甚至是item,那成为这样的极简:
<foreach>
#{item}
</foreach>
整体这样:
<select id="foreachTest" resultType="Blog">
select * from t_blog where id in
<foreach>
#{item}
</foreach>
</select>
简化成这样,谁还不敢直接写<foreach>
了?
来源:https://www.cnblogs.com/BillySir/p/16846366.html


猜你喜欢
- JMM与问题引入为啥先说JMM,因为CAS的实现类中维护的变量都被volatile修饰, 这个volatile 是遵循JMM规范(不是百分百
- Java 官网对Looper对象的说明:public class Looperextends ObjectClass used to run
- SpeSqliteManager4Android改动日志2023.2.14 完成SQLiteOpenHelper 2023.2.23 完成r
- 前言最近在网上看到一篇文章,里面说到:List<T>.FindAll的效率竟然比for循环还差,下面是文章的截图:我在上文代码基
- 本文实例讲述了Android测量每秒帧数Frames Per Second (FPS)的方法。分享给大家供大家参考。具体如下:MainThr
- maven简介及优势 maven是一个项目构建和管理的工具,提供了帮助管理 构建、文档、报告
- 前言不得不说SpringBoot的开发者是在为大众程序猿谋福利,把大家都惯成了懒汉,xml不配置了,连tomcat也懒的配置了,典型的一键启
- Spring之Bean的基本概念大家都知道Spring就是一个大型的工厂,而Spring容器中的Bean就是该工厂的产品.对于Spring容
- 在 Kotlin 中,reduce() 和 fold() 是函数式编程中常用的高阶函数。它们都是对集合中的元素进行聚合操作的函数,将一个集合
- 核心代码迁移相对顺利,大致流程如下:一、创建项目 1) cd cocos2d-x-3.0rc0;&nbs
- Java的源代码是以*.java的纯文本文件,可以使用任何文本编辑器来进行编写,但是这个源代码是无法执行的。执行源代码的这个任务就需要JDK
- 本文实例讲述了Android开发实现判断通知栏是否打开及前往设置页面的方法。分享给大家供大家参考,具体如下:项目中用到日程提醒功能,如果应用
- 前言我们都知道WebApi是依赖于Asp.Net MVC的 ,所以,想创建WebApi,就需要先创建一个Asp.Net MVC项目。但用Vi
- 需求:键盘录入一个月份,输出该月份对应的季节。一年有四季3,4,5 春季6,7,8 夏季9,
- synchronized关键字顾名思义,是用于同步互斥的作用的。这里精简的记一下它的使用方法以及意义:1. 当synchronized修饰
- Java中Static关键字的一些用法详解1. Static 修饰类属性,因为静态成员变量可以通过类名+属性名调用,非静态成员变量不能通过类
- 简介最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括:专门针对Java语言的:Kryo,FST等等跨语言的:
- 本文实例讲述了C#画图之饼图折线图的实现方法,是C#程序设计中非常实用的技巧。分享给大家供大家参考。具体方法分析如下:显示图像的控件定义如下
- 偶然在项目中用到播放视频时,需要横屏将视频全屏播放,所以需要监听屏幕的横竖屏切换事件。横竖屏切换监听效果: ConfigChang
- 您好,我是贾斯汀,欢迎又进来学习啦!【学习背景】学习Java的小伙伴,都知道想要提升个人技术水平,阅读JDK源码少不了,但是说实话还是有些难