先说结论,第2次分页失效的原因是第一次查询后将LocalThread清除了。
环境介绍
pagehelper版本
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
<!--对应的pagehelper实际如下-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
测试代码
@Test
public void page() {
PageHelper.startPage(2, 2);
List<PersonPO> personPOS = personMapper.selectAll();
//下面这条会查出所有数据,即不会分页。即只有紧跟着PageHelper后面的语句才会分页
List<PersonPO> personPOS1 = personMapper.selectAll();
PageInfo<PersonPO> pageInfo = new PageInfo<>(personPOS);
log.info("pageInfo:{}", pageInfo);
}
源码简要分析
主要类com.github.pagehelper.PageInterceptor
package com.github.pagehelper;
/**
* 删掉依赖及注释
*/
public class PageInterceptor implements Interceptor {
//这里略去其他的代码
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
//...
//调用方法判断是否需要进行分页,如果不需要,直接返回结果
if (!dialect.skip(ms, parameter, rowBounds)) {
//走这里,则分页
} else {
//走这里,则不会分页
}
return dialect.afterPage(resultList, parameter, rowBounds);
} finally {
if(dialect != null){
//这里最终会调到LOCAL_PAGE.remove()方法,后文有细说
dialect.afterAll();
}
}
}
//这里略去其他的代码
}
这里有两个关键方法
dialect.skip(ms, parameter, rowBounds)和dialect.afterAll(),都是定义在com.github.pagehelper.Dialect下
package com.github.pagehelper;
/**
* 删除注释和import
*/
public interface Dialect {
/**
* 跳过 count 和 分页查询
*
* @param ms MappedStatement
* @param parameterObject 方法参数
* @param rowBounds 分页参数
* @return true 跳过,返回默认查询结果,false 执行分页查询
*/
boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds);
//略过一些其他代码
/**
* 完成所有任务后
*/
void afterAll();
}
先看dialect.skip(ms, parameter, rowBounds)方法,PageHelper中的实现如下
@Override
public boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
if (ms.getId().endsWith(MSUtils.COUNT)) {
throw new RuntimeException("在系统中发现了多个分页插件,请检查系统配置!");
}
//继续跟进pageParams.getPage(parameterObject, rowBounds),可以看到调用的是LOCAL_PAGE.get(),而LOCAL_PAGE就是ThreadLocal<Page>。
//ps:在调用PageHelper.startPage()类似方法时,进行ThreadLocal的set;
//这里先给结论,第一次page不为null,第二次page为null。
//因此dialect.skip(ms, parameter, rowBounds)第一次调用时,返回false,第二次返回true。
//所以第一次进分页,第二次不会进分页
//第二次不进分页,那么就意味着,必然有地方调用了LOCAL_PAGE.remove(),也就是com.github.pagehelper.page.PageMethod的clearPage()方法;还记得dialect.afterAll()这个方法么,这里就是调用LOCAL_PAGE.remove()的入口。
Page page = pageParams.getPage(parameterObject, rowBounds);
if (page == null) {
return true;
} else {
//设置默认的 count 列
if (StringUtil.isEmpty(page.getCountColumn())) {
page.setCountColumn(pageParams.getCountColumn());
}
autoDialect.initDelegateDialect(ms);
return false;
}
}
再看dialect.afterAll(),PageHelper中的实现如下
@Override
public void afterAll() {
//这个方法即使不分页也会被执行,所以要判断 null
AbstractHelperDialect delegate = autoDialect.getDelegate();
if (delegate != null) {
delegate.afterAll();
autoDialect.clearDelegate();
}
//这里清除ThreadLocal
clearPage();
}
所以,紧跟着PageHelper.startPage(2, 2)后面的sql,会执行分页,而后的sql,都不会分页。