问题描述
最近在项目中使用PageHelper分页工具+Mybatis实现分页查询逻辑,但是发现分页逻辑并没有生效,代码片段如下:
public PageDTO<ChargeMetaDO> pageByParams(SearchChargeReq searchChargeRequest, int pageNo, int pageSize) {
PageHelper.startPage(pageNo, pageSize);
ChargePOExample example = buildExample(searchChargeRequest);
long total = chargeMapper.countByExample(example);
List<ChargeMetaDO> chargeMetaDoList = selectByExample(example);
return new PageDTO<>(total, chargeMetaDoList);
}
这段代码中传入了查询参数和分页信息,并返回总页数和当前页号的数据。但是实际执行的时候返回了全部的数据。
修复方式
排查的时候发现,count代码通过mybatis生成的sql语句中包含了分页参数,但是select语句却没有,因此将查询数据列表的请求放在计算总数据行数前面即可解决这个问题。
public PageDTO<ChargeMetaDO> pageByParams(SearchChargeReq searchChargeRequest, int pageNo, int pageSize) {
PageHelper.startPage(pageNo, pageSize);
ChargePOExample example = buildExample(searchChargeRequest);
// 需要先执行分页查询,再查询总数
List<ChargeMetaDO> chargeMetaDoList = selectByExample(example);
long total = chargeMapper.countByExample(example);
return new PageDTO<>(total, chargeMetaDoList);
}
原因分析
这里阅读了一下PageHepler,简单介绍一下PageHelper是如何将分页信息塞入当查询请求中的。
在调用PageHelper.startPage方法后最终会进入这段逻辑:
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
Page<E> page = new Page(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
Page<E> oldPage = getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
setLocalPage(page);
return page;
}
protected static void setLocalPage(Page page) {
LOCAL_PAGE.set(page);
}
这里要关注setLocalPage这个方法,这一行代码将分页的信息放入了当前线程上下文LOCAL_PAGE中,使得后续的同线程内查询可以从该变量中取到分页信息。
那么这个分页信息是在哪里被编入到SQL的呢?PageHelper工具实现了一个mybatis的拦截器PageInterceptor,在请求经过该拦截器时会读取LOCAL_PAGE中的分页信息并写入到SQL中。这里通过Dialect接口进行了抽象,Dialect接口定义了在经过该切面时的各种行为(如是否跳过,执行前操作,执行后操作等),并子类PageHelper实现分页逻辑。
public class PageInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
try {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement)args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds)args[2];
ResultHandler resultHa