一 前言
对于pageHelper 插件的使用呢,一直稀里糊涂,结果是分页能不能成功全靠运气。于是自己大致跟了下代码,虽然好多也没看懂,仅当自己的学习笔记啦。如果能帮到网友,就同乐了呀!
二 使用
@Override
public PageInfo<ReportInfo> physicalreportlist(ReportReqVo reportReqVo, Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<ReportInfo> req = reportMapper.findByCondition(reportReqVo);
PageInfo<ReportInfo> page = new PageInfo<>(req);
List<ReportInfo> result = new ArrayList<>();
if (CollectionUtils.isNotEmpty(req)) {
result= reportMapper.selectphysicalreportlist(req);
}
page.setList(result);
return page;
}
说明:对于单条sql查询,只要把查询语句紧挨着写在PageHelper.startPage(pageNum, pageSize)下面。如果一条sql查询不能搞定,也是将第一次查询的sql紧挨着写在pageHelper.startPage(pageNum, pageSize)下面。多次查询,肯定有个初始数据源。像我这里,req就是初始数据,查出所有id,供第二次查询用。
注意:PageHelper.startpage()执行后就开始分页,第一行sql每次都执行pageSize。按道理讲,第二次查询和第一次查询的
条数应该是一样的,不然,你分页信息是每页10条,但是实际情况少于10条。所以这里要注意两点:
1 第二次最好根据id查询
2 left join也可能影响第二次查询结果的条数。
这里可以做个试验。将这里第二查询语句,假如只取前5条,你会在页面看到这样的信息:分页信息都是对的,只是每页分页的数据只有5条!
建议:最好按照下面的步骤写!
1
PageHelper.startPage(pageNum, pageSize);
List<ReportInfo> req = reportMapper.findByCondition(reportReqVo);
PageInfo<ReportInfo> page = new PageInfo<>(req);
2 做二次处理(可能不需要)
3 return page
三 原理简单分析
第一行:PageHelpler.startPage(pageNum, pageSize) 就会把分页初始信息准备好,还有个默认参数count(默认值true),查询出total(总条数)。
截取部分代码:注意这里的setLocalPage()
Page<E> page = new Page<E>(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
//当已经执行过orderBy的时候
Page<E> oldPage = getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
setLocalPage(page);
return page;
第二行:执行了第一行代码后,他已经控制了第二条查询语句每次查询的条数。原来,在执行sql前,插件使用使用了拦截器,保证这条sql每次只执行pageSize条。具体实现不是很清楚。而且每次都会查询出count
截取部分代码:
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 resultHandler = (ResultHandler) args[3];
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
//由于逻辑关系,只会进入一次
if(args.length == 4){
//4 个参数时
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 个参数时
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
List resultList;
//调用方法判断是否需要进行分页,如果不需要,直接返回结果
if (!dialect.skip(ms, parameter, rowBounds)) {
//反射获取动态参数
String msId = ms.getId();
Configuration configuration = ms.getConfiguration();
Map<String, Object> additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql);
//判断是否需要进行 count 查询
if (dialect.beforeCount(ms, parameter, rowBounds)) {
String countMsId = msId + countSuffix;
Long count;
//先判断是否存在手写的 count 查询
MappedStatement countMs = getExistedMappedStatement(configuration, countMsId);
if(countMs != null){
count = executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
} else {
countMs = msCountMap.get(countMsId);
//自动创建
if (countMs == null) {
//根据当前的 ms 创建一个返回值为 Long 类型的 ms
countMs = MSUtils.newCountMappedStatement(ms, countMsId);
msCountMap.put(countMsId, countMs);
}
count = executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler);
}
//处理查询总数
//返回 true 时继续分页查询,false 时直接返回
if (!dialect.afterCount(count, parameter, rowBounds)) {
//当查询总数为 0 时,直接返回空的结果
return dialect.afterPage(new ArrayList(), parameter, rowBounds);
}
}
第三行:截取了部分代码:
if (list instanceof Page) {
Page page = (Page) list;
this.pageNum = page.getPageNum();
this.pageSize = page.getPageSize();
this.pages = page.getPages();
this.list = page;
this.size = page.size();
this.total = page.getTotal();
//由于结果是>startRow的,所以实际的需要+1
if (this.size == 0) {
this.startRow = 0;
this.endRow = 0;
} else {
this.startRow = page.getStartRow() + 1;
//计算实际的endRow(最后一页的时候特殊)
this.endRow = this.startRow - 1 + this.size;
}
....
....
}
这一句主要目的,我认为就是获取整个分页信息!
这里只是冰山一角,有好多没看懂,list强制转成page对象后,debug中根据获取不到pages total信息,是我太菜了呀!
四 补充
先这样吧,以后再补充,不过一定要按照我上面的步骤写分页。有不对的,请指明!