SpringBoot系列(12): Mybatis采坑之For Each In大數查詢


作者: 修羅debug
版權聲明:本文為博主原創文章,遵循 CC 4.0 by-sa 版權協議,轉載請附上原文出處鏈接和本聲明。

摘要:在開發企業級應用項目業務模塊期間,相信很多小伙伴都使用過Mybatis,畢竟SSM早已橫行天下。然后,在使用Myabtis期間,如果經常需要用到 IN 查詢,那么特別需要注意的是“大批數據量的查詢”,使用不當,很可能會出現內存溢出以及其他奇奇怪怪的錯誤。

內容:在企業級應用項目開發過程中,Mybatis的For  Each  In查詢功能相信各位小伙伴都遇見過,特別是在一些多表關聯,根據Id  IN查詢的場景,相關各位小伙伴多多少少都是有見過的!

下面,Debug將給各位小伙伴演示一下當 IN 查詢的數據量過大時,采用For  Each遍歷、拼接實現的方式的弊端。

(1)直接上一個單元測試方法的代碼吧:

    @Test
public void method3() throws Exception{
//待批量查詢的 列元素 列表
List<Integer> list=userMapper.selectAllIds();
//查詢具體的實體對象列表
List<User> users=userMapper.selectByIds(list);
log.info("--獲取查詢結果列表:{}",users);
}

其中,userMapper.selectAllIds();方法是前往數據庫表codes拿出所有的item_id,然后調用userMapper.selectByIds(list); 方法查詢出 指定的 id 列表內的用戶實體對象列表信息。其源代碼分別如下所示:  

  <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數據庫表中的所有id一共有 37w 多條,如下圖所示:  


接下來,我們點擊“運行該單元測試方法”,一起見識一下會發生什么事情:  



從該運行結果報錯的信息來看,Packet for query is too large (7910580 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable

表示 待查詢的數據量太大了,已經超過了指定可允許的容量了(其實也就間接影響了查詢鏈接的斷開與否了?。?/span>

解決方法一可以嘗試去設置 “max_allowed_packet”屬性的值,但是這只是治標不治本,不能從根本上進行解決!而且,For Each 在數據量大的情況也著實會消耗更多的內存、建立的與數據庫鏈接所占用的資源毫無疑問會面臨“斷開”的風險。。。

于是乎,我們巧妙的想到了第二種方法,即很多人詬病的“$查詢”,在下面大家會看到“曾經被別人討厭的東西,如今看來卻起到了很大的作用!”

(2)調整后的源代碼如下所示:

    @Test
public void method4() throws Exception{
//待批量查詢的 列元素 列表
List<Integer> list=userMapper.selectAllIds();

//利用mybatis $ 靜態取值的特性,將其轉化為 元素拼接的String 常量值
List<User> users=userMapper.selectByStrIds(Joiner.on(",").join(list));
log.info("--獲取查詢結果列表:{}",users);
}

我們在這里利用的正是 $ 靜態特性,將查詢出來的大批數據量的 列元素 列表轉化為 String常量值,塞到數據庫Mybatis查詢時直接靜態獲取、復制即可,而不需要 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>

點擊運行,會發現,一點毛事都木有了,而且運行得出的結果還很快(各位小伙伴可以將數據量提升到 百萬 級別然后進行自測試試?。?/span>


好了,本篇文章我們就介紹到這里了,在數據量過大的情況下,建議各位小伙伴采用這種方式進行實現!  其他相關的技術,感興趣的小伙伴可以關注底部Debug的技術公眾號,或者加Debug的微信,拉你進“微信版”的真正技術交流群!一起學習、共同成長!  


補充:

1、本文涉及到的相關的源代碼可以到此地址,check出來進行查看學習:

https://gitee.com/steadyjack/SpringBootTechnology

2、目前Debug已將本文所涉及的內容整理錄制成視頻教程,感興趣的小伙伴可以前往觀看學習:

https://www.fightjava.com/web/index/course/detail/5

3、關注一下Debug的技術微信公眾號,最新的技術文章、技術課程以及技術專欄將會第一時間在公眾號發布哦!