不规范使用PageHelper导致线程污染

背景

存在一个业务需要通过多段sql将数据查询出来,之后将全部数据指定规则排序,最后根据pageSizepageNumber进行分页。由于PageHelper官方说明:==MyBatis 查询方法前调用 PageHelper.startPage 静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。==所以不太符合该业务的需求,需要自己去手动分页,为了标准化的PageHelper分页的出参,我去百度复制了一份别人的代码。

public static  PageInfo listPageInfo(List 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 pageResult = new LinkedList();
        if (arrayList.size() > pageStart) {
            pageResult = arrayList.subList(pageStart, pageEnd);
        }
        PageInfo pageInfo = new PageInfo(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

image-20210423104806738.png

但是实际上,日记打印出来limit1 后面还跟随着limit ?,?,明显出现了问题。
image-20210423104920986.png

BUG的真正原因

实际上经过排查,真正原因是因为调用自定义分页出现了问题,PageHelper.startPage(pageNum, pageSize);使用了之后并没有消费,分页参数一直保存在线程中,当这个线程再次调用的时候,导致莫名奇妙的加上limit关键字。

如何查到这个BUG真正原因

官网文档上说:PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelperfinally 代码段中自动清除了 ThreadLocal 存储的对象。

因为随机加上limit关键字,所以可以查看ThreadLocal LOCAL_PAGE值的变化,只有当线程复用的时候才会出现LOCAL_PAGE已被实例化。

image-20210423110814791.png

官方文档地址:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
官网文档明确说明了什么时候会导致不安全的分页相关内容:
image-20210423111448026.png

总结

在日后使用分页过程中如果出现无缘无故出现分页,则大概率的说明之前的PageHelper.startPage并没有被消耗掉,所以在使用了PageHelper.startPage后需要紧接着 MyBatis 查询方法。

你可能感兴趣的:(不规范使用PageHelper导致线程污染)