不規(guī)范使用PageHelper導(dǎo)致線程污染

背景

存在一個(gè)業(yè)務(wù)需要通過多段sql將數(shù)據(jù)查詢出來,之后將全部數(shù)據(jù)指定規(guī)則排序,最后根據(jù)pageSizepageNumber進(jìn)行分頁。由于PageHelper官方說明:==MyBatis 查詢方法前調(diào)用 PageHelper.startPage 靜態(tài)方法即可,緊跟在這個(gè)方法后的第一個(gè)MyBatis 查詢方法會被進(jìn)行分頁。==所以不太符合該業(yè)務(wù)的需求,需要自己去手動分頁,為了標(biāo)準(zhǔn)化的PageHelper分頁的出參,我去百度復(fù)制了一份別人的代碼。

public static <T> PageInfo<T> listPageInfo(List<T> arrayList, Integer pageNum, Integer pageSize) {
        if(arrayList == null || pageNum == null || pageSize == null){
            return null;
        }
        //實(shí)現(xiàn)list分頁
        PageHelper.startPage(pageNum, pageSize);
        int pageStart = pageNum == 1 ? 0 : (pageNum - 1) * pageSize;
        int pageEnd = Math.min(arrayList.size(), pageSize * pageNum);
        List<T> pageResult = new LinkedList<T>();
        if (arrayList.size() > pageStart) {
            pageResult = arrayList.subList(pageStart, pageEnd);
        }
        PageInfo<T> pageInfo = new PageInfo<T>(pageResult);
        //獲取PageInfo其他參數(shù)
        pageInfo.setTotal(arrayList.size());
        int endRow = pageInfo.getEndRow() == 0 ? 0 : (pageNum - 1) * pageSize + pageInfo.getEndRow() + 1;
        pageInfo.setEndRow(endRow);
        boolean hasNextPage = arrayList.size() > pageSize * pageNum;
        pageInfo.setHasNextPage(hasNextPage);
        boolean hasPreviousPage = pageNum != 1;
        pageInfo.setHasPreviousPage(hasPreviousPage);
        pageInfo.setIsFirstPage(!hasPreviousPage);
        boolean isLastPage = arrayList.size() > pageSize * (pageNum - 1) && arrayList.size() <= pageSize * pageNum;
        pageInfo.setIsLastPage(isLastPage);
        int pages = arrayList.size() % pageSize == 0 ? arrayList.size() / pageSize : (arrayList.size() / pageSize) + 1;
        pageInfo.setNavigateLastPage(pages);
        int[] navigatePageNums = new int[pages];
        for (int i = 1; i < pages; i++) {
            navigatePageNums[i - 1] = i;
        }
        pageInfo.setNavigatepageNums(navigatePageNums);
        int nextPage = pageNum < pages ? pageNum + 1 : 0;
        pageInfo.setNextPage(nextPage);
        pageInfo.setPageNum(pageNum);
        pageInfo.setPageSize(pageSize);
        pageInfo.setPages(pages);
        pageInfo.setPrePage(pageNum - 1);
        pageInfo.setSize(pageInfo.getList().size());
        int starRow = arrayList.size() < pageSize * pageNum ? 1 + pageSize * (pageNum - 1) : 0;
        pageInfo.setStartRow(starRow);
        return pageInfo;
    }

BUG的表面原因

用了別人分頁的代碼,出現(xiàn)數(shù)據(jù)丟失的情況,隨機(jī)的在我sql語句加上limit關(guān)鍵字。

正常情況我的SQL語句,只存在limit 1

image-20210423104806738.png

但是實(shí)際上,日記打印出來limit1 后面還跟隨著limit ?,?,明顯出現(xiàn)了問題。
image-20210423104920986.png

BUG的真正原因

實(shí)際上經(jīng)過排查,真正原因是因?yàn)檎{(diào)用自定義分頁出現(xiàn)了問題,PageHelper.startPage(pageNum, pageSize);使用了之后并沒有消費(fèi),分頁參數(shù)一直保存在線程中,當(dāng)這個(gè)線程再次調(diào)用的時(shí)候,導(dǎo)致莫名奇妙的加上limit關(guān)鍵字。

如何查到這個(gè)BUG真正原因

官網(wǎng)文檔上說:PageHelper 方法使用了靜態(tài)的 ThreadLocal 參數(shù),分頁參數(shù)和線程是綁定的。只要你可以保證在 PageHelper 方法調(diào)用后緊跟 MyBatis 查詢方法,這就是安全的。因?yàn)?PageHelperfinally 代碼段中自動清除了 ThreadLocal 存儲的對象。

因?yàn)殡S機(jī)加上limit關(guān)鍵字,所以可以查看ThreadLocal<Page> LOCAL_PAGE值的變化,只有當(dāng)線程復(fù)用的時(shí)候才會出現(xiàn)LOCAL_PAGE已被實(shí)例化。

image-20210423110814791.png

官方文檔地址:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
官網(wǎng)文檔明確說明了什么時(shí)候會導(dǎo)致不安全的分頁相關(guān)內(nèi)容:
image-20210423111448026.png

總結(jié)

在日后使用分頁過程中如果出現(xiàn)無緣無故出現(xiàn)分頁,則大概率的說明之前的PageHelper.startPage并沒有被消耗掉,所以在使用了PageHelper.startPage后需要緊接著 MyBatis 查詢方法。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容