背景
存在一个业务需要通过多段sql将数据查询出来,之后将全部数据指定规则排序,最后根据pageSize
和pageNumber
进行分页。由于PageHelper官方说明:==MyBatis 查询方法前调用 PageHelper.startPage
静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。==所以不太符合该业务的需求,需要自己去手动分页,为了标准化的PageHelper分页的出参,我去百度复制了一份别人的代码。
public static <T> PageInfo<T> listPageInfo(List<T> arrayList, Integer pageNum, Integer pageSize) {
if(arrayList == null || pageNum == null || pageSize == null){
return null;
}
//实现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其他参数
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的表面原因
用了别人分页的代码,出现数据丢失的情况,随机的在我sql语句加上limit关键字。
正常情况我的SQL语句,只存在limit 1
但是实际上,日记打印出来limit1 后面还跟随着
limit ?,?
,明显出现了问题。BUG的真正原因
实际上经过排查,真正原因是因为调用自定义分页出现了问题,PageHelper.startPage(pageNum, pageSize);
使用了之后并没有消费,分页参数一直保存在线程中,当这个线程再次调用的时候,导致莫名奇妙的加上limit关键字。
如何查到这个BUG真正原因
官网文档上说:PageHelper
方法使用了静态的 ThreadLocal
参数,分页参数和线程是绑定的。只要你可以保证在 PageHelper
方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper
在 finally
代码段中自动清除了 ThreadLocal
存储的对象。
因为随机加上limit关键字,所以可以查看ThreadLocal<Page> LOCAL_PAGE
值的变化,只有当线程复用的时候才会出现LOCAL_PAGE
已被实例化。
官方文档地址:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
官网文档明确说明了什么时候会导致不安全的分页相关内容:
总结
在日后使用分页过程中如果出现无缘无故出现分页,则大概率的说明之前的PageHelper.startPage
并没有被消耗掉,所以在使用了PageHelper.startPage
后需要紧接着 MyBatis 查询方法。