今天使用了分页插件,并将其整合到SpringBoot中。各种遇到了个别问题,现在记录下。吃一垫长一智。
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>1.2.5version>
dependency>
接着在application.yml中配置相关参数
#pagehelper
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
returnPageInfo: check
参数说明
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
#方式1
PageHelper.startPage(1, 10);
List<ScoreGoodsCategory> goodsCategoryList = mapper.selectByPage();
int totalCount=(int) ((Page)goodsCategoryList).getTotal();
# 方式二
PageHelper.offsetPage(1, 10);
List<ScoreGoodsCategory> goodsCategoryList = mapper.selectByPage();
PageInfo<ScoreGoodsCategory> pageInfo = new PageInfo<>(goodsCategoryList);
int totalCount=(int) pageInfo.getTotal();
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>最新版本version>
dependency>
在Spring的配置文件中配置拦截器插件
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
params=count=countSql
value>
<value>
helperDialect=mysql
value>
<value>
reasonable=true
value>
<value>
supportMethodsArguments=true
value>
<value>
returnPageInfo=check
value>
property>
bean>
array>
property>
bean>
配置好之后,使用同上。
其最核心的方法就在拦截器中,那我们首先看看拦截器中的拦截方法。该方法主要做了两件事,1. 统计总条数,2.对原始的SQL进行改写使其可以分页。
PageInterceptor
类的intercept
方法是拦截器的总入口方法。
首先,我们来看看统计总条数的相关代码。
//PageInterceptor 类
//设置count的sql的id,在原始的msId后面加上_COUNT后缀。
String countMsId = msId + countSuffix;
// 生成统计sql的入口方法
count = executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler);
接下来我们就来看看executeAutoCount 方法
/**
* 执行自动生成的 count 查询
*
* @param executor sql执行器
* @param countMs
* @param parameter
* @param boundSql
* @param rowBounds
* @param resultHandler 结果处理器
* @return
* @throws IllegalAccessException
* @throws SQLException
*/
private Long executeAutoCount(Executor executor, MappedStatement countMs,
Object parameter, BoundSql boundSql,
RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException, SQLException {
Map<String, Object> additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql);
//创建 count 查询的缓存 key
CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
//调用方言获取 count sql
String countSql = dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
//countKey.update(countSql);
BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
//当使用动态 SQL 时,可能会产生临时的参数,这些参数需要手动设置到新的 BoundSql 中
for (String key : additionalParameters.keySet()) {
countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
//执行 count 查询
Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
Long count = (Long) ((List) countResultList).get(0);
return count;
}
如上,方法的注释比较详实,此处我们主要介绍下第二步调用方言获取 count sql。dialect 是一个接口类,PageHelper
是其的一个实现类。接着我们来看看PageHelper中的getCountSql方法。
//*PageHelper
@Override
public String getCountSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey countKey) {
return autoDialect.getDelegate().getCountSql(ms, boundSql, parameterObject, rowBounds, countKey);
}
如上,在PageHelper的getCountSql直接把请求给了AbstractHelperDialect(通过autoDialect.getDelegate() 获得) 的getCountSql 方法。我们接着往下看
//*AbstractHelperDialect
@Override
public String getCountSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey countKey) {
Page<Object> page = getLocalPage();
String countColumn = page.getCountColumn();
if (StringUtil.isNotEmpty(countColumn)) {
return countSqlParser.getSmartCountSql(boundSql.getSql(), countColumn);
}
return countSqlParser.getSmartCountSql(boundSql.getSql());
}
如上,countsql语句的生成逻辑最终落在了CountSqlParser类,该类是一个通用的sql解析类。最后我们来看看CountSqlParser类的getSmartCountSql方法。
/**
* 获取智能的countSql
*
* @param sql 我们传入的需要分页的sql
* @param name 列名,默认 0
* @return
*/
public String getSmartCountSql(String sql, String name) {
//解析SQL
Statement stmt = null;
//特殊sql不需要去掉order by时,使用注释前缀
if(sql.indexOf(KEEP_ORDERBY) >= 0){
return getSimpleCountSql(sql);
}
try {
// 对sql 进行分解
stmt = CCJSqlParserUtil.parse(sql);
} catch (Throwable e) {
//无法解析的用一般方法返回count语句
return getSimpleCountSql(sql);
}
Select select = (Select) stmt;
SelectBody selectBody = select.getSelectBody();
try {
//处理body-去order by
processSelectBody(selectBody);
} catch (Exception e) {
//当 sql 包含 group by 时,不去除 order by
return getSimpleCountSql(sql);
}
//处理with-去order by
processWithItemsList(select.getWithItemsList());
//处理为count查询
sqlToCount(select, name);
String result = select.toString();
return result;
}
调试结果:
大致流程如下(举例说明):
对sql 进行分页的入口逻辑还是在PageInterceptor
类的intercept
方法中。话不多说,上代码。
//生成分页的缓存 key
CacheKey pageKey = cacheKey;
//处理参数对象
parameter = dialect.processParameterObject(ms, parameter, boundSql, pageKey);
//调用方言获取分页 sql
String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
//设置动态参数
for (String key : additionalParameters.keySet()) {
pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
//执行分页查询
resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
如上,主要四步:
List<String> taxNoList = juhePayClearingRecordMapper.pageClearingTaxNo(tradeTime);
if (taxNoList == null) {
return null;
}
taxNoList = taxNoList.stream().filter(b -> StringUtils.isNotBlank(b)).collect(Collectors.toList());
PageInfo<String> pageInfo = new PageInfo<>(taxNoList);
PagePOJO<String> taxNoInfo = new PagePOJO<>();
taxNoInfo.setACount((int) pageInfo.getTotal());
如上,先通过SQL语句插入集合taxNoList,然后对taxNoList做了一个去除空字符串的处理,再获取总条数时,数据不对
这是因为查询出来的taxNoList,实际上是一个Page对象,所以获取总条数是调用的((Page)list).getTotal()
来获取的。而如果对taxNoList进行处理之后,他就变成了一个普通的ArrayList对象了。所以,获取的总条数total不对。
2. 坑二、在PageHelper.startPage
方法之后添加了代码,同样的会导致不能分页
首先感谢liuzh同志开发出了这款好用的插件,代码很规范,插件很好用。本文首先介绍了Mybatis-PageHelper插件的整合与使用,接着介绍了相关原理,主要是统计总条数的实现原理。希望对读者朋友们有所帮助。
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md