PageHelper使用分页错乱问题

PageMethod源码

public abstract class PageMethod {
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
    protected static boolean DEFAULT_COUNT = true;

    public PageMethod() {
    }

    protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }

    public static <T> Page<T> getLocalPage() {
        return (Page)LOCAL_PAGE.get();
    }

    public static void clearPage() {
        LOCAL_PAGE.remove();
    }
}

PageHelper 是较为常用的分页插件,通过实现 Mybatis 的 Interceptor 接口完成对 query sql 的动态分页。

从PageMethod中可以看出,分页参数由 ThreadLocal 进行保存。

大致分为下面几步

  1. 设置 page 参数
  2. 执行 query 方法
  3. Interceptor 接口中校验 ThreadLocal 中是否存在有设置的 page 参数
  4. 存在 page 参数,重新生成 count sql 和 page sql,并执行查询。不存在 page 参数,直接返回 查询结果
  5. 执行 LOCAL_PAGE.remove() 清除 page 参数

问题场景

 PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
 // 可以换成其它业务逻辑
 int i = 1 / 0;
 List<User> dataList = this.baseMapper.getList();

观察上述的执行过程,可以发现,如果在第 1 步和第 2 步 之间发生异常,那么 LOCAL_PAGE 中当前线程对应的 page 参数并不会 remove。
如果不适用线程池那还好,线程在执行完毕后会被销毁。

用了线程池,当前线程执行完毕,并不会被销毁,会将当前线程存放到池中,标记为空闲状态,以便后续使用。

后续使用未被清空参数的线程时。

由于线程 的 threadLocals 依旧存在有值,即使没有设置page参数,线程中仍然有参数,从而生成 count sql 和 page sql有问题!!!

从而影响我们的正常查询。

解决方案

1. 贴紧【第一个查询有效】
 PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
 List<User> dataList = this.baseMapper.getList();
2. try–catch–finally
try {
       PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
       List<User> dataList = this.baseMapper.getList();
  } finally {
    PageHelper.clearPage();
}
3. 使用前先清空
protected void startPage(int pageNum, int pageSize) {
    PageHelper.clearPage();
    PageHelper.startPage(pageNum, pageSize);
}
4. request请求

请求完成时,清空当前线程的threadLocals 属性值,也就是执行 LOCAL_PAGE.remove() 即可。
实现方式:

使用 aop,对所有 controller 进行处理
实现 HandlerInterceptor 或者 WebRequestInterceptor 对 request 请求的拦截器接口,通过 afterCompletion 方法执行 LOCAL_PAGE.remove() 。

//Interceptor
public class PageWebInterceptor implements HandlerInterceptor {
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // PageHelper.clearPage() 内部调用 LOCAL_PAGE.remove()
        PageHelper.clearPage();
    }
}
    
//配置类自动生效
@Configuration
public class PageWebAutoConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new PageWebInterceptor());
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值