1.问题描述以及业务代码展示
2.源码角度分析分页失效原因分析以及处理方案
查询资讯列表信息中需要支持分页查询,使用的是pagehelper进行查询。执行查询资讯列表信息之前需要进行查询点赞信息。按照指定页数和每页显示条数查询出来的资讯列表信息不准确,结果都是查询出来所有资讯列表信息。下面贴一下业务的伪代码
需要的依赖:
com.github.pagehelper pagehelper-spring-boot-starter 1.2.3 org.mybatis mybatis org.mybatis mybatis-spring
TestPageHelper .java:
@RestController
@RequestMapping("/testPageHelper")
public class TestPageHelper {
@Autowired
private PageHelperServiceImpl pageHelperService;
@GetMapping
public ResultVo> findNews(Integer currentPage,Integer pageSize){
PageHelper.startPage(currentPage,pageSize);
List newsList = pageHelperService.findNews();
PageInfo newsPageInfo = new PageInfo(newsList);
newsPageInfo.setList(newsList);
return ResultVoUtil.success(newsPageInfo);
}
}
PageHelperServiceImpl.java:
@Service
public class PageHelperServiceImpl {
@Autowired
private NewsMapper newsMapper;
// 查询资讯信息
public List findNews(){
// 查询点赞信息
List like = newsMapper.findLike();
// 查询资讯信息
List news = newsMapper.findNews();
return news;
}
}
mapper查询逻辑不进行展示,仅用作说明问题过程。
实际测试发现:当前页为1,每页显示2条,查询的点赞列表信息分页正常,查询出资讯列表信息为6条(总条数);当前页为2,每页显示2条,查询的点赞列表信息分页正常,查询出资讯列表信息也是6条(总条数);分页失效!!
pageHelper关键组件中有一个拦截器 PageInterceptor,每一个涉及数据库查询的mapper方法都会被该拦截器进行拦截,里面执行的主要逻辑是查询总数处理和执行分页处理。
关于执行总条数处理,细心的同学应该在控制台看过打印的业务sql前面一般都会有一个select count(0)操作,如下图:
这个源码中都是可以进行设置的,继续往下看。pageHelper最核心的作用是执行分页,底层实现的逻辑其实就是在业务sql后面根据当前页以及每页显示的条数拼接limit。下面直接看源码。
PageInterceptor.java中拦截处理逻辑:
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
// 省略部分代码
//调用方法判断是否需要进行分页,如果不需要,直接返回结果,最终是判断是否存在ThreadLocal,继续往下看
if (!dialect.skip(ms, parameter, rowBounds)) {
// 省略部分代码
//判断是否需要进行 count 查询,就是决定控制台是否显示select count(0)
if (dialect.beforeCount(ms, parameter, rowBounds)) {
// 处理总条数的逻辑省略
}
//返回 true 时继续分页查询,false 时直接返回
if (!dialect.afterCount(count, parameter, rowBounds)) {
//当查询总数为 0 时,直接返回空的结果
return dialect.afterPage(new ArrayList(), parameter, rowBounds);
}
}
//判断是否需要进行分页查询
if (dialect.beforePage(ms, parameter, rowBounds)) {
//生成分页的缓存 key
CacheKey pageKey = cacheKey;
//处理参数对象
parameter = dialect.processParameterObject(ms, parameter, boundSql, pageKey);
//调用方言获取分页 sql
String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
// 组装BoundSql,实际就是在业务代码后面拼接limit ?,?
BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
//执行分页查询
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
} else {
//不执行分页的情况下,也不执行内存分页
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
}
} else {
//rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
return dialect.afterPage(resultList, parameter, rowBounds);
} finally {
// 最终执行的是清空ThreadLocal操作,LOCAL_PAGE.remove()
dialect.afterAll();
}
}
一般在业务代码中都会写PageHelper.startPage(currentPage,pageSize),它的主要作用是设置ThreadLocal
public abstract class PageMethod {
protected static final ThreadLocal LOCAL_PAGE = new ThreadLocal();
protected static void setLocalPage(Page page) {
LOCAL_PAGE.set(page);
}
public static Page getLocalPage() {
return LOCAL_PAGE.get();
}
public static void clearPage() {
LOCAL_PAGE.remove();
}
// 省略部分代码
}
PageInterceptor.java中判断是否支持分页主要是根据能否存在ThreadLocal
TestPageHelper.java:
@RestController
@RequestMapping("/testPageHelper")
public class TestPageHelper {
@Autowired
private PageHelperServiceImpl pageHelperService;
@GetMapping
public ResultVo> findNews(Integer currentPage,Integer pageSize){
List newsList = pageHelperService.findNews(currentPage,pageSize);
PageInfo newsPageInfo = new PageInfo(newsList);
newsPageInfo.setList(newsList);
return ResultVoUtil.success(newsPageInfo);
}
}
PageHelperServiceImpl.java:
@Service
public class PageHelperServiceImpl {
@Autowired
private NewsMapper newsMapper;
// 查询资讯信息
public List findNews(Integer currentPage,Integer pageSize){
PageHelper.startPage(currentPage,pageSize);
// 查询点赞信息
List like = newsMapper.findLike();
PageHelper.startPage(currentPage,pageSize);
// 查询资讯列表
List news = newsMapper.findNews();
return news;
}
}
总结:对指定mapper查询支持分页,前面一定要有PageHelper.startPage(currentPage,pageSize),不能有其他mapper查询,否则会失效!
欢迎评论区点赞留言,相互交流,共同进步!



