PageHelper使用分頁(yè)錯(cuò)亂問(wèn)題
PageMethod源碼
PageHelper 是較為常用的分頁(yè)插件,通過(guò)實(shí)現(xiàn) Mybatis 的 Interceptor 接口完成對(duì) query sql 的動(dòng)態(tài)分頁(yè)。
從PageMethod中可以看出,分頁(yè)參數(shù)由 ThreadLocal 進(jìn)行保存。
大致分為下面幾步
設(shè)置 page 參數(shù)
執(zhí)行 query 方法
Interceptor 接口中校驗(yàn) ThreadLocal 中是否存在有設(shè)置的 page 參數(shù)
存在 page 參數(shù),重新生成 count sql 和 page sql,并執(zhí)行查詢(xún)。不存在 page 參數(shù),直接返回 查詢(xún)結(jié)果
執(zhí)行 LOCAL_PAGE.remove() 清除 page 參數(shù)
問(wèn)題場(chǎng)景
PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
// 可以換成其它業(yè)務(wù)邏輯
int i = 1 / 0;
List<User> dataList = this.baseMapper.getList();
觀察上述的執(zhí)行過(guò)程,可以發(fā)現(xiàn),如果在第 1 步和第 2 步 之間發(fā)生異常,那么 LOCAL_PAGE 中當(dāng)前線程對(duì)應(yīng)的 page 參數(shù)并不會(huì) remove。 如果不適用線程池那還好,線程在執(zhí)行完畢后會(huì)被銷(xiāo)毀。
用了線程池,當(dāng)前線程執(zhí)行完畢,并不會(huì)被銷(xiāo)毀,會(huì)將當(dāng)前線程存放到池中,標(biāo)記為空閑狀態(tài),以便后續(xù)使用。
后續(xù)使用未被清空參數(shù)的線程時(shí)。
由于線程 的 threadLocals 依舊存在有值,即使沒(méi)有設(shè)置page參數(shù),線程中仍然有參數(shù),從而生成 count sql 和 page sql有問(wèn)題?。?!
從而影響我們的正常查詢(xún)。
解決方案
1. 貼緊【第一個(gè)查詢(xún)有效】
2. try--catch--finally
3. 使用前先清空
4. request請(qǐng)求
請(qǐng)求完成時(shí),清空當(dāng)前線程的threadLocals 屬性值,也就是執(zhí)行 LOCAL_PAGE.remove() 即可。 實(shí)現(xiàn)方式:
使用 aop,對(duì)所有 controller 進(jìn)行處理 實(shí)現(xiàn) HandlerInterceptor 或者 WebRequestInterceptor 對(duì) request 請(qǐng)求的攔截器接口,通過(guò) afterCompletion 方法執(zhí)行 LOCAL_PAGE.remove() 。