PageHelper 源码分析(ThreadLocal<Page> )

123 阅读3分钟

1.构建 通用分页 DTO

public class PageParam<T> {
 
/**
 * 每几页
 */
@ApiModelProperty(value = "每几页")
private int pageNum;
/**
 * 每页大小
 */
@ApiModelProperty(value = "每页大小")
private int pageSize;

public int getPageNum() {
    return pageNum;
}

@ApiModelProperty(value = "关键字")
private String keywords;

@ApiModelProperty(value = "排序字段")
private String orderBy;


@ApiModelProperty(value = "是否查询总条数")
private YesNoEnum isCount;
 

public <T> Page<T> buildPage() {
    return buildPage(false);
}

public <T> Page<T> buildPage(boolean count) {
    if (this.pageNum == 0) {
        this.pageNum = 1;
    }
    if (this.pageSize == 0) {
        this.pageSize = 15;
    }
    // 如果为否,则不查询总条件
    if (YesNoEnum.NO.equals(isCount)) {
        count = false;
    }
    // 传入:PageHelper
    Page<T> page = PageHelper.startPage(this.pageNum, this.pageSize, count);
    if (!StringUtil.isStringParamNotLegal(orderBy)) {
        if (checkOrderBy(orderBy)) {
            page.setOrderBy(orderBy);
        }
    }
    return page;
}

2.传入: 分页参数 --> 传入:PageHelper Page page = PageHelper.startPage(this.pageNum, this.pageSize, count);

public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count) {
   return startPage(pageNum, pageSize, count, (Boolean)null, (Boolean)null);
}
public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
   Page<E> page = new Page(pageNum, pageSize, count);
   page.setReasonable(reasonable);
   page.setPageSizeZero(pageSizeZero);
   Page<E> oldPage = getLocalPage();
   if (oldPage != null && oldPage.isOrderByOnly()) {
       page.setOrderBy(oldPage.getOrderBy());
   }

   setLocalPage(page);
   return page;
}
  1. 将分页参数放进 ThreadLocal LOCAL_PAGE setLocalPage(page);
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
  1. Mybatis - 通用分页拦截器

@Intercepts(
       {
               @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
               @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
       }
)
public class PageInterceptor implements Interceptor {


@Override 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]; } checkDialectExists();

    List resultList;
    //调用方法判断是否需要进行分页,如果不需要,直接返回结果
    if (!dialect.skip(ms, parameter, rowBounds)) {
        //判断是否需要进行 count 查询
        if (dialect.beforeCount(ms, parameter, rowBounds)) {
            //查询总数
            Long count = count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            //处理查询总数,返回 true 时继续分页查询,false 时直接返回
            if (!dialect.afterCount(count, parameter, rowBounds)) {
                //当查询总数为 0 时,直接返回空的结果
                return dialect.afterPage(new ArrayList(), parameter, rowBounds);
            }
        }
        
        resultList = ExecutorUtil.pageQuery(dialect, executor,
                ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
                
    } else {
    
      *  //rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
        resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);*
        
        
    }
    
    
    
    return dialect.afterPage(resultList, parameter, rowBounds);
    
    
} finally {
    if(dialect != null){
        dialect.afterAll();
    }
}

}


5.针对 PageHelper 的实现
@Override
public Object processParameterObject(MappedStatement ms, Object parameterObject, BoundSql boundSql, CacheKey pageKey) {
    //处理参数
    Page page = getLocalPage();
    //如果只是 order by 就不必处理参数
    if (page.isOrderByOnly()) {
        return parameterObject;
    }
    Map<String, Object> paramMap = null;
    if (parameterObject == null) {
        paramMap = new HashMap<String, Object>();
    } else if (parameterObject instanceof Map) {
        //解决不可变Map的情况
        paramMap = new HashMap<String, Object>();
        paramMap.putAll((Map) parameterObject);
    } else {
        paramMap = new HashMap<String, Object>();
        //动态sql时的判断条件不会出现在ParameterMapping中,但是必须有,所以这里需要收集所有的getter属性
        //TypeHandlerRegistry可以直接处理的会作为一个直接使用的对象进行处理
        boolean hasTypeHandler = ms.getConfiguration().getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass());
        MetaObject metaObject = MetaObjectUtil.forObject(parameterObject);
        //需要针对注解形式的MyProviderSqlSource保存原值
        if (!hasTypeHandler) {
            for (String name : metaObject.getGetterNames()) {
                paramMap.put(name, metaObject.getValue(name));
            }
        }
        //下面这段方法,主要解决一个常见类型的参数时的问题
        if (boundSql.getParameterMappings() != null && boundSql.getParameterMappings().size() > 0) {
            for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
                String name = parameterMapping.getProperty();
                if (!name.equals(PAGEPARAMETER_FIRST)
                        && !name.equals(PAGEPARAMETER_SECOND)
                        && paramMap.get(name) == null) {
                    if (hasTypeHandler
                            || parameterMapping.getJavaType().equals(parameterObject.getClass())) {
                        paramMap.put(name, parameterObject);
                        break;
                    }
                }
            }
        }
    }
    return processPageParameter(ms, paramMap, page, boundSql, pageKey);
}
  1. 处理分页结果
@Override
public Object afterPage(List pageList, Object parameterObject, RowBounds rowBounds) {


   Page page = getLocalPage();
   
   
   if (page == null) {
       return pageList;
   }
   
   page.addAll(pageList);
   
   if (!page.isCount()) {
       page.setTotal(-1);
   } else if ((page.getPageSizeZero() != null && page.getPageSizeZero()) && page.getPageSize() == 0) {
       page.setTotal(pageList.size());
   } else if(page.isOrderByOnly()){
       page.setTotal(pageList.size());
   }
   return page;
}

6.使用分页结果 CommonPage.toPage(page);

public static <T> CommonPage<T> toPage(Page<T> page) {
    CommonPage<T> result = new CommonPage<T>();
    result.setTotalPage(page.getPages());
    result.setPageNum(page.getPageNum());
    result.setPageSize(page.getPageSize());
    result.setRecordCount(page.getTotal());
    result.setRows(page.getResult());
    return result;
}



public List getResult() { return this; }

  1. 分页完毕,及时关闭流处理 Page extends** ArrayList** implements Closeable
@Override
public void close() {
    PageHelper.clearPage();
}

/**

  • 移除本地变量 */ public static void clearPage() { LOCAL_PAGE.remove(); }