背景
存在一個(gè)業(yè)務(wù)需要通過多段sql將數(shù)據(jù)查詢出來,之后將全部數(shù)據(jù)指定規(guī)則排序,最后根據(jù)pageSize
和pageNumber
進(jìn)行分頁还绘。由于PageHelper官方說明:==MyBatis 查詢方法前調(diào)用 PageHelper.startPage
靜態(tài)方法即可窗价,緊跟在這個(gè)方法后的第一個(gè)MyBatis 查詢方法會(huì)被進(jìn)行分頁。==所以不太符合該業(yè)務(wù)的需求碰煌,需要自己去手動(dòng)分頁舒岸,為了標(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
但是實(shí)際上眯杏,日記打印出來limit1 后面還跟隨著
limit ?,?
,明顯出現(xiàn)了問題壳澳。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)?PageHelper
在 finally
代碼段中自動(dòng)清除了 ThreadLocal
存儲(chǔ)的對(duì)象。
因?yàn)殡S機(jī)加上limit關(guān)鍵字终佛,所以可以查看ThreadLocal<Page> LOCAL_PAGE
值的變化俊嗽,只有當(dāng)線程復(fù)用的時(shí)候才會(huì)出現(xiàn)LOCAL_PAGE
已被實(shí)例化。
官方文檔地址:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
官網(wǎng)文檔明確說明了什么時(shí)候會(huì)導(dǎo)致不安全的分頁相關(guān)內(nèi)容:
總結(jié)
在日后使用分頁過程中如果出現(xiàn)無緣無故出現(xiàn)分頁查蓉,則大概率的說明之前的PageHelper.startPage
并沒有被消耗掉乌询,所以在使用了PageHelper.startPage
后需要緊接著 MyBatis 查詢方法。