PageHelper引發(fā)的幽靈數(shù)據(jù)問題解析
前言
最近測試反饋一個問題,某個查詢?nèi)啃畔⒌慕涌?,有時候返回全量數(shù)據(jù),符合預(yù)期,但是偶爾又只返回1條數(shù)據(jù),簡直就是“見鬼”了,究竟是為什么出現(xiàn)這樣的“幽靈數(shù)據(jù)”呢?
大膽猜測
首先我們看了下這對代碼的業(yè)務(wù)邏輯,非常的簡單,總共沒有幾行代碼,也沒有分頁邏輯,代碼如下:
public List<SdSubscription> findAll() {
return sdSubscriptionMapper.selectAll();
}
那么究竟是咋回事呢?講道理不可能出現(xiàn)這種情況的啊,不要慌,我們加點日志,將日志級別調(diào)整為DEBUG,讓日志飛一段時間。
public List<SdSubscription> findAll() {
log.info("find the sub start .....");
List<SdSubscription> subs = sdSubscriptionMapper.selectAll();
log.info("find the sub end .....");
return subs;
}
果不其然,日志中出現(xiàn)了奇奇怪怪的分頁參數(shù),如下圖所示:

果然是PageHelper這個開源框架搞的鬼,我想大家都用過吧,分頁非常方便,那么究竟為什么別人都沒問題,單單就我會出現(xiàn)問題呢?
PageHelper工作原理
為了回答上面的疑問,我們先看看PageHelper框架的工作原理吧。
PageHelper 是一個開源的 MyBatis 分頁插件,它可以幫助開發(fā)者在查詢數(shù)據(jù)時,快速的實現(xiàn)分頁功能。
PageHelper 的工作原理可以簡單概括為以下幾個步驟:
- 在需要進行分頁的查詢方法前,調(diào)用
PageHelper的靜態(tài)方法startPage(),設(shè)置當前頁碼和每頁顯示的記錄數(shù)。它會將分頁信息放到線程的ThreadLocal中,那么在線程的任何地方都可以訪問了。 - 當查詢方法執(zhí)行時,
PageHelper會自動攔截查詢語句,如果發(fā)現(xiàn)線程的ThreadLocal中有分頁信息,那么就會在其前后添加分頁語句,例如MySQL中的LIMIT語句。 - 查詢結(jié)果將被包裝在
Page對象中返回,該對象包含分頁信息和查詢結(jié)果列表。 - 在查詢方法執(zhí)行完畢后,會在
finally中清除線程ThreadLocal中的分頁信息,避免分頁設(shè)置對其他查詢方法的影響。
PageHelper 的實現(xiàn)原理主要依賴于攔截器技術(shù)和反射機制,通過攔截查詢語句并動態(tài)生成分頁語句,實現(xiàn)了簡單、高效、通用的分頁功能。具體源碼在下圖的類中,非常容易看懂。

明白了PageHelper的工作原理后,反復檢查代碼,都沒有調(diào)用過startPage,debug查看ThreadLocal中也沒有分頁信息啊,懵逼中。那我看看別人寫的添加分頁參數(shù)的代碼吧,不看不知道,一看嚇一跳。

原來有位“可愛”的同事竟然在查詢后,加了一個分頁,就是把分頁信息放到線程的ThreadLocal中。
那大家是不是有疑問,丁是丁,矛是矛,你的線程關(guān)我何事?這就要說到我們的tomcat了。
Tomcat請求流程
其實這就涉及到我們的tomcat相關(guān)知識了,我們一個瀏覽器發(fā)一個接口請求,經(jīng)過我們的tomcat的,究竟是一個什么樣的流程呢?
- 客戶端發(fā)送
HTTP請求到Tomcat服務(wù)器。 Tomcat的HTTP連接器(Connector)接收到請求,將連接請求交給線程池Executor處理,解析它,然后將請求轉(zhuǎn)發(fā)給對應(yīng)的Web應(yīng)用程序。Tomcat的Web應(yīng)用程序容器(Container)接收到請求,根據(jù)請求的URL找到對應(yīng)的Servlet。
關(guān)于tomcat中使用線程池提交瀏覽器的連接請求的源碼如下:

從而得知,你的連接請求是從線程池從拿的,而拿到的這個線程恰好是一個“臟線程”,在ThreadLocal中放了分頁信息,導致你這邊出現(xiàn)問題。
總結(jié)
后來追問了同事具體原因,才發(fā)現(xiàn)是粗心導致的。有些bug總是出現(xiàn)的莫名其妙,就像生活一樣。所以關(guān)鍵的是我們在使用一些開源框架的時候一定要掌握底層實現(xiàn)的原理、核心的機制,這樣才能夠在解決一些問題的時候有據(jù)可循。
以上就是PageHelper引發(fā)的幽靈數(shù)據(jù)問題解析的詳細內(nèi)容,更多關(guān)于PageHelper幽靈數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springboot+AOP實現(xiàn)返回數(shù)據(jù)提示語國際化的示例代碼
這篇文章主要介紹了Springboot+AOP實現(xiàn)返回數(shù)據(jù)提示語國際化的示例代碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-07-07
IntelliJ IDEA配置java環(huán)境及解決IDEA不能直接運行單個JAVA文件的問題
這篇文章主要介紹了IntelliJ IDEA配置java環(huán)境及解決IDEA不能直接運行單個JAVA文件的問題,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
為何HashSet中使用PRESENT而不是null作為value
這篇文章主要介紹了為何HashSet中使用PRESENT而不是null作為value,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
SpringBoot3利用AOP實現(xiàn)IP黑名單功能
在Web應(yīng)用開發(fā)中,控制對特定IP地址的訪問權(quán)限是一個常見的需求,通過實現(xiàn)IP黑白名單功能,我們可以允許某些IP地址訪問應(yīng)用,同時拒絕其他IP地址的訪問,本文將詳細介紹SpringBoot3利用AOP實現(xiàn)IP黑名單功能,并附上相應(yīng)的代碼片段,需要的朋友可以參考下2024-09-09
遠程調(diào)用@FeignClient注解屬性使用詳解
這篇文章主要為大家介紹了遠程調(diào)用@FeignClient注解屬性使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10
關(guān)于Spring項目對JDBC的支持與基本使用詳解
這段時間一直在觀看Spring框架,所以下面這篇文章主要給大家介紹了關(guān)于Spring項目對JDBC的支持與基本使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2018-11-11
Spring Boot整合SSE實時通信的問題小結(jié)
本文介紹了服務(wù)器發(fā)送事件(Server-Sent Events,SSE)技術(shù),其主要特點包括單向數(shù)據(jù)流、自動重連、自定義事件類型等,SSE適用于實時更新場景,如新聞推送、評論系統(tǒng)等,感興趣的朋友跟隨小編一起看看吧2025-01-01

