帶你重新認(rèn)識(shí)MyBatis的foreach
用了MyBatis的同行,應(yīng)該見(jiàn)過(guò)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>
難記
有沒(méi)有人跟我一樣,覺(jué)得
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
這一串很啰嗦?每次寫(xiě)<foreach>
都因?yàn)橛洸蛔『团掠洸焕?,要查找和?fù)制現(xiàn)有的來(lái)改。假如可以簡(jiǎn)化,就不至于要這樣了。
一項(xiàng)項(xiàng)來(lái)分析:
foreach
這個(gè)是xml標(biāo)簽,少不了。collection
可以通過(guò)探測(cè)參數(shù)類型+數(shù)量,從而在大多數(shù)情況下,知道對(duì)應(yīng)哪個(gè)參數(shù)而省略。index
這個(gè)就更加不用寫(xiě)了,幸好它真的是用時(shí)才寫(xiě),不用不寫(xiě)。item
假如缺省值就是item
,則可能會(huì)有一些沖突,比如某個(gè)表名叫item
,會(huì)不會(huì)讓人掉坑里才豁然知道緣由?想不清楚。open
難道不是通常都是"("嗎?但只要不寫(xiě)open
,缺省值是沒(méi)有,即是空串,很無(wú)語(yǔ),強(qiáng)迫我們幾乎每個(gè)foreach都要寫(xiě)open="("
separator
難道不是絕大多數(shù)都是","嗎?缺省值也是空串,更無(wú)語(yǔ)close
和open
同理。
空集合問(wèn)題
還有一個(gè)問(wèn)題,當(dāng)傳入的集合是空集的時(shí)候,比如上面這個(gè)例子,某些情況計(jì)算出來(lái)的list是空集合。按道理,既然list是id的集合,空集合意味著應(yīng)該select到0條記錄。但結(jié)果卻是報(bào)SQL語(yǔ)法錯(cuò)誤。原因是生成的SQL長(zhǎng)這樣:
select * from t_blog where id in ; -- 為了明顯,我補(bǔ)了個(gè)分號(hào)
是的,按SQL語(yǔ)法,in后面的小括號(hào)是不能沒(méi)有內(nèi)容的,小括號(hào)本身也是不能省的,抓狂。
在以前,我接觸的項(xiàng)目有DAO層(指java interface mapper之上的一層),我會(huì)在DAO層先判斷,若list是Empty,直接返回空集,否則再執(zhí)行mybatis的查詢。后來(lái)的一些項(xiàng)目,推崇簡(jiǎn)單化,沒(méi)有DAO層,Service直接調(diào)用java interface mapper。把這個(gè)“判空返回空”的邏輯寫(xiě)在Service就顯得別扭。難道MyBatis就不能優(yōu)雅地解決這個(gè)問(wèn)題嗎?
解法
直到后來(lái)看到了這種寫(xiě)法:
<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 ("
,與一開(kāi)始的id in
寫(xiě)在foreach外面有什么不同呢?經(jīng)過(guò)實(shí)驗(yàn),結(jié)論是這樣的:
- 當(dāng)list非空時(shí),兩者并無(wú)區(qū)別。
- 當(dāng)list為空時(shí),既不生成
open
值,也不生成close
值!
所以,當(dāng)list為空時(shí),SQL就是:select * from t_blog
,也就沒(méi)有違反語(yǔ)法。但是查到的是全部的記錄,而不是預(yù)期的“0條記錄”。
改進(jìn)為這樣寫(xiě)
<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
的兩邊加上小括號(hào)。
<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,都會(huì)生成正確語(yǔ)法和功能的SQL語(yǔ)句。有興趣的朋友可自行推導(dǎo)。
優(yōu)雅的解法
但我覺(jué)得上面的寫(xiě)法不夠優(yōu)雅,經(jīng)過(guò)實(shí)驗(yàn),找到一種我認(rèn)為更優(yōu)雅的寫(xiě)法:
<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
沒(méi)了,缺省就是空串#{item}
后面有了個(gè)逗號(hào)(,
)close
里多了個(gè)-1
是這樣的思路,我們要解決的是in
后面是空的問(wèn)題,假如在list為empty時(shí),也能“塞”入一個(gè)不存在的值,比如這里規(guī)定id不可能是-1,那么生成的sql就是where id in (-1)
,語(yǔ)法沒(méi)錯(cuò),且查不到數(shù)據(jù)。而當(dāng)list非空時(shí),生成的sql如where id in (1,2,3,-1)
,雖然有-1
,但不影響查出來(lái)的結(jié)果。只是本來(lái)3
是最后一個(gè),不需要加逗號(hào),由于后面固定有-1
,所以每個(gè)#{item}
后面固定要有逗號(hào),如果逗號(hào)放在separator,則只出現(xiàn)在各個(gè)id之間,放在#{item}
就可以出現(xiàn)在每個(gè)item后了。
通過(guò)此案例,回頭一看,思路一下子就打開(kāi)了。open
里的寫(xiě)法可以很靈活,只要符合SQL語(yǔ)法的,理論上都可以往open
里放。separator
、close
也一樣??赡茏髡呔褪且?yàn)檫@些靈活的用法,所以干脆讓它們的缺省值是空串。這本無(wú)標(biāo)準(zhǔn),所以不能說(shuō)這樣做就是對(duì)或錯(cuò)的。
一種簡(jiǎn)化<foreach>的設(shè)想
但如果允許我修改設(shè)計(jì)的話,我會(huì)讓open
的缺省值為"("
,separator
的缺省值為", "
,close
的缺省值為")"
。因?yàn)榇蠖鄶?shù)情況下它們的值就是這樣。假如特殊情況下,就想要它的值為空串,可以這么寫(xiě)open=""
,何嘗不可。這樣做的好處是,通常情況下foreach會(huì)很簡(jiǎn)潔:
<foreach collection="list" item="item"> #{item} </foreach>
例如,像文章開(kāi)頭分析的那樣,還可以去掉collection,甚至是item,那成為這樣的極簡(jiǎn):
<foreach> #{item} </foreach>
整體這樣:
<select id="foreachTest" resultType="Blog"> select * from t_blog where id in <foreach> #{item} </foreach> </select>
簡(jiǎn)化成這樣,誰(shuí)還不敢直接寫(xiě)<foreach>
了?
總結(jié)
本文提出了一種簡(jiǎn)化<foreach>
寫(xiě)法的設(shè)想,更重要的是通過(guò)解決空集時(shí)生成的SQL語(yǔ)法問(wèn)題,更深刻地理解MyBatis的foreach的生成機(jī)制。打開(kāi)思路,更靈活地利用open
、separator
、close
,得到符合預(yù)期的SQL,還兼顧到代碼的優(yōu)雅。
到此這篇關(guān)于重新認(rèn)識(shí)MyBatis的foreach的文章就介紹到這了,更多相關(guān)MyBatis的foreach內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java并發(fā)編程之CountDownLatch源碼解析
這篇文章主要介紹了Java并發(fā)編程之CountDownLatch源碼解析,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java并發(fā)編程的小伙伴們有很好的幫助,需要的朋友可以參考下2021-04-04JS求多個(gè)數(shù)組的重復(fù)數(shù)據(jù)
這篇文章主要介紹了JS求多個(gè)數(shù)組的重復(fù)數(shù)據(jù)的辦法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Java基于正則表達(dá)式實(shí)現(xiàn)xml文件的解析功能詳解
這篇文章主要介紹了Java基于正則表達(dá)式實(shí)現(xiàn)xml文件的解析功能,結(jié)合實(shí)例形式分析了java使用正則表達(dá)式針對(duì)xml文件節(jié)點(diǎn)的相關(guān)操作技巧,需要的朋友可以參考下2017-08-08SpringBoot整合第三方技術(shù)的實(shí)現(xiàn)
本文主要介紹了SpringBoot整合第三方技術(shù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02一個(gè)處理用戶登陸的servlet簡(jiǎn)單實(shí)例
這篇文章主要介紹了一個(gè)處理用戶登陸的servlet簡(jiǎn)單實(shí)例,可通過(guò)servlet實(shí)現(xiàn)處理用戶登錄的功能,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-01-01springboot rabbitmq整合rabbitmq之消息持久化存儲(chǔ)問(wèn)題
這篇文章主要介紹了springboot rabbitmq整合rabbitmq之消息持久化存儲(chǔ)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09關(guān)于Java整合RabbitMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)的7種通訊方式
這篇文章主要介紹了關(guān)于Java整合RabbitMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)的7種通訊方式,消息中間件是基于隊(duì)列與消息傳遞技術(shù),在網(wǎng)絡(luò)環(huán)境中為應(yīng)用系統(tǒng)提供同步或異步、可靠的消息傳輸?shù)闹涡攒浖到y(tǒng),需要的朋友可以參考下2023-05-05Spring中一個(gè)少見(jiàn)的引介增強(qiáng)IntroductionAdvisor
這篇文章主要為大家介紹了Spring中一個(gè)少見(jiàn)的引介增強(qiáng)IntroductionAdvisor實(shí)戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Java基礎(chǔ)教程之基本類型數(shù)據(jù)類型、包裝類及自動(dòng)拆裝箱
這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)教程之基本類型數(shù)據(jù)類型、包裝類及自動(dòng)拆裝箱的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06