关于pagehelper组件
参考资料:官方文档
- pagehelper组件是一个分页插件,就是用于分页操作的。
- 它与mybatis-plus自带的分页插件有何差异性?
- 为啥要先select count(0) ?能不能跳过这一步
查看源码
- 在调用时一般直接使用
PageHelper.startPage(pageNum,pageSize)
方法,先会new一个Page- 该方法中默认设置
DEFAULT_COUNT=True
- 该值为该插件设置了默认的count语句是否执行
- 将该值设为false后,就不会启动默认的count语句,就只执行一条语句
- 该方法中默认设置
- 然后设置对应参数获取localPage,确认localpage是否为空,如果不为空,将localpage的orderby值赋给新的page。
- 关于localPage:
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
- 关于ThreadLocal:解决线程安全问题,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题
- 关于localPage:
- 然后执行
setLocalPage(page)
- LOCAL_PAGE 设置为page
- 返回page
PageHelper.startPage的参数详解
protected static boolean DEFAULT_COUNT = true;
public static <E> Page<E> startPage(int pageNum, int pageSize) {
return startPage(pageNum, pageSize, DEFAULT_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) {
//源码中该方法的参数部分
//可以看出,该方法中若传参只有pageNum和pageSize时,count语句默认为开启状态,resonable和pageSizeZero默认为null。
//其中resonable和pageSizeZero可以通过springboot的application.properties对这两个参数进行设定
//resonable为分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
//pageSizeZero 默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
- 分页合理化参数的效果必须要默认的count开启,才能实现,不然它还是按照输入的参数进行查询。
当pageNum超过总数时
mp自带的分页组件
- mp自带的分页组件,当查询的pageNum超过总数时,它会只执行
select count(*) from table
,正常返回total,返回records为空。
pagehelper插件
- 当pagehelper的count为true,且reasonable也为true时,会返回最后一页的值。
- 当pagehelper的count为false时,会执行
SELECT 列名1,列名2 FROM user LIMIT ?, ?
,返回结果为空,返回total为-1。
源码部分有两个拦截器
@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}
)})
- 其中MappedStatement为mapper文件中的一个节点
- 由于myBatis 中涉及到动态 SQL 的原因,缓存项的 key 不能仅仅通过一个 String 来表示,所以通过CacheKey 来封装缓存的 key 值,CacheKey 可以封装多个影响缓存项的因素。
- BoundSql保存Sql语句的对象。
- 这里是使用Mybatis中的插件来进行拦截的
- 实现Interceptor接口,并指定了想要拦截的方法签名
- 这里的两个方法签名只存在参数上的区别,这对应了两种情况,是否有cachekey和boundsql。
- 这里拦截的是Executor实例中所有的select方法调用,其中Executor是负责执行底层映射语句的内部对象。【还可以选择update,commit,rollback等。
- 实现Interceptor接口,并指定了想要拦截的方法签名
QueryInterceptor
从代码上看,只是对查询执行需要的6个参数进行了补全。
PageInterceptor
从代码上看,拦截器由以下几个方法组成:
intercept() 拦截后主要操作在这里
setProperties() 顾名思义,设置properties
plugin() 插件----不知道在干嘛
intercept部分
-
首先,和queryInterceptor中一样,将查询执行需要的6个参数进行了补全。
-
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) { boundSql = ms.getBoundSql(parameter); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { cacheKey = (CacheKey)args[4]; boundSql = (BoundSql)args[5]; }
-
-
然后,检查dialect是否存在
-
dialect理解为分页类型?比如Mysql、DB2、Oracle等
-
private void checkDialectExists() { if (this.dialect == null) { synchronized(this.default_dialect_class) { if (this.dialect == null) { this.setProperties(new Properties()); } } } }
-
-
接着检查是否处于count前状态,如果是count前就先count,然后检查是否是page后状态,如果是就return
if (this.dialect.beforeCount(ms, parameter, rowBounds)) { Long count = this.count(executor, ms, parameter, rowBounds, resultHandler, boundSql); if (!this.dialect.afterCount(count, parameter, rowBounds)) { Object var12 = this.dialect.afterPage(new ArrayList(), parameter, rowBounds); return var12; } } public boolean beforeCount(MappedStatement ms, Object parameterObject, RowBounds rowBounds) { Page page = this.getLocalPage(); return !page.isOrderByOnly() && page.isCount(); }
-
检查完count状态后,执行pageQuery操作
public static <E> List<E> pageQuery(Dialect dialect, Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, CacheKey cacheKey) throws SQLException { if (!dialect.beforePage(ms, parameter, rowBounds)) { return executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql); } else { parameter = dialect.processParameterObject(ms, parameter, boundSql, cacheKey); String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, cacheKey); BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(), pageSql, boundSql.getParameterMappings(), parameter); Map<String, Object> additionalParameters = getAdditionalParameter(boundSql); Iterator var12 = additionalParameters.keySet().iterator(); while(var12.hasNext()) { String key = (String)var12.next(); pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key)); } return executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, pageBoundSql); } }