Mybatis PagerHelper 实现原理

首先说下使用.

@Override
public PageInfo selectDocByPage1(int currentPage, int pageSize) {
PageHelper.startPage(currentPage, pageSize);
List docs = docMapper.selectByPageAndSelections();
PageInfo pageInfo = new PageInfo<>(docs);
return pageInfo;
}

按照以往经验,以 MySQL 为例,要分页,则必须构造出 limit 或者是 offset 这样的东西. 那么 PageHelper 的原理也是一样.

下面我们从 Interceptor 开始一步步看下 Mybatis 是如何使用的.

PageHelper.startPage.

/**
* 开始分页
*
* @param pageNum 页码
* @param pageSize 每页显示数量
* @param count 是否进行count查询
*/
public static Page startPage(int pageNum, int pageSize, boolean count) {
return startPage(pageNum, pageSize, count, null, null);
}

startPage 方法的主要作用是构造了一个 Page 对象,存放分页的基础信息. 然后放在 threadLocal 中.
/**
* 开始分页
*
* @param pageNum 页码
* @param pageSize 每页显示数量
* @param count 是否进行count查询
* @param reasonable 分页合理化,null时用默认配置
* @param pageSizeZero true且pageSize=0时返回全部结果,false时分页,null时用默认配置
*/
public static Page startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
Page page = new Page(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
//当已经执行过orderBy的时候
Page oldPage = getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
setLocalPage(page);
return page;
}

PageInterceptor -> 拦截器 -> 方法入口.

重写 setProperties 方法,获得 dialect. 默认 com.github.pagehelper.PageHelper.

然后看下 PageHelper 类的 skip 方法(即决定是否跳过分页)
public boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
if (ms.getId().endsWith(MSUtils.COUNT)) {
throw new RuntimeException(“在系统中发现了多个分页插件,请检查系统配置!”);
}
// 如果没有设置分页的话,则 page 为空,返回 true,跳过,不分页.
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;
}
}

需要注意 PageHelper 中的 PageAutoDialect 属性,这个属性中配置了多种数据库的方言分页. 例如:
registerDialectAlias(“hsqldb”, HsqldbDialect.class);
registerDialectAlias(“h2”, HsqldbDialect.class);
registerDialectAlias(“postgresql”, HsqldbDialect.class);
registerDialectAlias(“phoenix”, HsqldbDialect.class);

    registerDialectAlias("mysql", MySqlDialect.class);
    registerDialectAlias("mariadb", MySqlDialect.class);
    registerDialectAlias("sqlite", MySqlDialect.class);

    registerDialectAlias("herddb", HerdDBDialect.class);

综上,PageHelper 类中的 skip 方法很重要,它决定了是否分页,以及使用的分页对象.

beforeCount 方法.
/**
* 执行分页前,返回 true 会进行 count 查询,false 会继续下面的 beforePage 判断
*
* @param ms MappedStatement
* @param parameterObject 方法参数
* @param rowBounds 分页参数
* @return
*/
boolean beforeCount(MappedStatement ms, Object parameterObject, RowBounds rowBounds);

countAfter 方法.
/**
* 执行完 count 查询后
*
* @param count 查询结果总数
* @param parameterObject 接口参数
* @param rowBounds 分页参数
* @return true 继续分页查询,false 直接返回
*/
boolean afterCount(long count, Object parameterObject, RowBounds rowBounds);

执行分页查询,在执行分页查询的时候,会使用到方言中的 getPageSql 来动态构造分页 sql 语句.
@Override
public String getPageSql(String sql, Page page, CacheKey pageKey) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 140);
sqlBuilder.append(“SELECT * FROM (SELECT TMP_PAGE.*,ROWNUMBER() OVER() AS ROW_ID FROM ( “);
sqlBuilder.append(sql);
sqlBuilder.append(” ) AS TMP_PAGE) TMP_PAGE WHERE ROW_ID BETWEEN ? AND ?”);
return sqlBuilder.toString();
}

问题:基于 RowBounds 的分页有使用到吗?

目前 rowbounds 包下的类是没有被使用到的.

你可能感兴趣的:(Mybatis)