SpringBoot系列(12): Mybatis采坑之For Each In大數(shù)查詢
作者:
修羅debug
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 by-sa 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。
摘要:在開發(fā)企業(yè)級應(yīng)用項(xiàng)目業(yè)務(wù)模塊期間,相信很多小伙伴都使用過Mybatis,畢竟SSM早已橫行天下。然后,在使用Myabtis期間,如果經(jīng)常需要用到 IN 查詢,那么特別需要注意的是“大批數(shù)據(jù)量的查詢”,使用不當(dāng),很可能會(huì)出現(xiàn)內(nèi)存溢出以及其他奇奇怪怪的錯(cuò)誤。
內(nèi)容:在企業(yè)級應(yīng)用項(xiàng)目開發(fā)過程中,Mybatis的For Each In查詢功能相信各位小伙伴都遇見過,特別是在一些多表關(guān)聯(lián),根據(jù)Id IN查詢的場景,相關(guān)各位小伙伴多多少少都是有見過的!
下面,Debug將給各位小伙伴演示一下當(dāng) IN 查詢的數(shù)據(jù)量過大時(shí),采用For Each遍歷、拼接實(shí)現(xiàn)的方式的弊端。
(1)直接上一個(gè)單元測試方法的代碼吧:
@Test
public void method3() throws Exception{
//待批量查詢的 列元素 列表
List<Integer> list=userMapper.selectAllIds();
//查詢具體的實(shí)體對象列表
List<User> users=userMapper.selectByIds(list);
log.info("--獲取查詢結(jié)果列表:{}",users);
}
其中,userMapper.selectAllIds();方法是前往數(shù)據(jù)庫表codes拿出所有的item_id,然后調(diào)用userMapper.selectByIds(list);
方法查詢出 指定的 id 列表內(nèi)的用戶實(shí)體對象列表信息。其源代碼分別如下所示:
<select id="selectAllIds" resultType="java.lang.Integer">
SELECT item_id FROM codes
</select>
<select id="selectByIds" resultType="com.debug.springboot.model.entity.User" parameterType="java.util.List">
SELECT <include refid="Base_Column_List"/>
FROM user
<where>
id IN
<foreach collection="list" item="item" separator="," open="(" close=")" index="">
#{item,jdbcType=VARCHAR}
</foreach>
</where> </select>
值得一提的,codes數(shù)據(jù)庫表中的所有id一共有 37w 多條,如下圖所示:
接下來,我們點(diǎn)擊“運(yùn)行該單元測試方法”,一起見識(shí)一下會(huì)發(fā)生什么事情:
從該運(yùn)行結(jié)果報(bào)錯(cuò)的信息來看,Packet for query is too large (7910580 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable
表示 待查詢的數(shù)據(jù)量太大了,已經(jīng)超過了指定可允許的容量了(其實(shí)也就間接影響了查詢鏈接的斷開與否了?。?/span>
解決方法一可以嘗試去設(shè)置 “max_allowed_packet”屬性的值,但是這只是治標(biāo)不治本,不能從根本上進(jìn)行解決!而且,F(xiàn)or Each 在數(shù)據(jù)量大的情況也著實(shí)會(huì)消耗更多的內(nèi)存、建立的與數(shù)據(jù)庫鏈接所占用的資源毫無疑問會(huì)面臨“斷開”的風(fēng)險(xiǎn)。。。
于是乎,我們巧妙的想到了第二種方法,即很多人詬病的“$查詢”,在下面大家會(huì)看到“曾經(jīng)被別人討厭的東西,如今看來卻起到了很大的作用!”
(2)調(diào)整后的源代碼如下所示:
@Test
public void method4() throws Exception{
//待批量查詢的 列元素 列表
List<Integer> list=userMapper.selectAllIds();
//利用mybatis $ 靜態(tài)取值的特性,將其轉(zhuǎn)化為 元素拼接的String 常量值
List<User> users=userMapper.selectByStrIds(Joiner.on(",").join(list));
log.info("--獲取查詢結(jié)果列表:{}",users);
}
我們在這里利用的正是 $ 靜態(tài)特性,將查詢出來的大批數(shù)據(jù)量的 列元素 列表轉(zhuǎn)化為 String常量值,塞到數(shù)據(jù)庫Mybatis查詢時(shí)直接靜態(tài)獲取、復(fù)制即可,而不需要 For Each 遍歷、賦值、拼接再 IN 查詢了,其源代碼如下所示:
<select id="selectByStrIds" resultType="com.debug.springboot.model.entity.User">
SELECT <include refid="Base_Column_List"/>
FROM user
WHERE id IN (${ids})
</select>
點(diǎn)擊運(yùn)行,會(huì)發(fā)現(xiàn),一點(diǎn)毛事都木有了,而且運(yùn)行得出的結(jié)果還很快(各位小伙伴可以將數(shù)據(jù)量提升到 百萬 級別然后進(jìn)行自測試試?。?/span>
好了,本篇文章我們就介紹到這里了,在數(shù)據(jù)量過大的情況下,建議各位小伙伴采用這種方式進(jìn)行實(shí)現(xiàn)! 其他相關(guān)的技術(shù),感興趣的小伙伴可以關(guān)注底部Debug的技術(shù)公眾號(hào),或者加Debug的微信,拉你進(jìn)“微信版”的真正技術(shù)交流群!一起學(xué)習(xí)、共同成長!
補(bǔ)充:
1、本文涉及到的相關(guān)的源代碼可以到此地址,check出來進(jìn)行查看學(xué)習(xí):
https://gitee.com/steadyjack/SpringBootTechnology
2、目前Debug已將本文所涉及的內(nèi)容整理錄制成視頻教程,感興趣的小伙伴可以前往觀看學(xué)習(xí):
https://www.fightjava.com/web/index/course/detail/5
3、關(guān)注一下Debug的技術(shù)微信公眾號(hào),最新的技術(shù)文章、技術(shù)課程以及技術(shù)專欄將會(huì)第一時(shí)間在公眾號(hào)發(fā)布哦!