Github PageHelper 原理解析
// 组装查询条件
ArticleVO articleVO = new ArticleVO();
articleVO.setAuthor("刘慈欣"); // 初始化返回类
// ResponsePages类是这样一种返回类,其中包括返回代码code和返回消息msg
// 还包括返回的数据和分页信息
// 其中,分页信息就是 com.github.pagehelper.Page<?> 类型
ResponsePages<List<ArticleVO>> responsePages = new ResponsePages<>(); // 这里为了简单,写死分页参数。正确的做法是从查询条件中获取
// 假设需要获取第1页的数据,每页20条记录
// com.github.pagehelper.Page<?> 类的基本字段如下
// pageNum: 当前页
// pageSize: 每页条数
// total: 总记录数
// pages: 总页数
com.github.pagehelper.Page<?> page = PageHelper.startPage(1, 20); // 根据条件获取文章列表
List<ArticleVO> articleList = articleMapper.getArticleListByCondition(articleVO); // 设置返回数据
responsePages.setData(articleList); // 设置分页信息
responsePages.setPage(page);
/**
* 开始分页
*
* @param pageNum 页码
* @param pageSize 每页显示数量
* @param count 是否进行count查询
* @param reasonable 分页合理化,null时用默认配置
* @param pageSizeZero true 且 pageSize=0 时返回全部结果,false时分页, null时用默认配置
*/
public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
Page<E> page = new Page<E>(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
// 当已经执行过orderBy的时候
Page<E> oldPage = SqlUtil.getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
SqlUtil.setLocalPage(page);
return page;
}
package com.github.pagehelper.util;
... public class BaseSqlUtil {
// 省略其他代码 private static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>(); /**
* 从 ThreadLocal<Page> 中获取 page
*/
public static <T> Page<T> getLocalPage() {
return LOCAL_PAGE.get();
} /**
* 将 page 设置到 ThreadLocal<Page>
*/
public static void setLocalPage(Page page) {
LOCAL_PAGE.set(page);
} // 省略其他代码
}
package org.apache.ibatis.binding;
... public class MapperMethod { public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
// 省略
} else if (SqlCommandType.UPDATE == command.getType()) {
// 省略
} else if (SqlCommandType.DELETE == command.getType()) {
// 省略
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
/**
* 获取多条记录
*/
result = executeForMany(sqlSession, args);
} else if ...
// 省略
} else if (SqlCommandType.FLUSH == command.getType()) {
// 省略
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
... return result;
}
}
# sqlSessionFactory 中的重要信息 sqlSessionFactory
configuration
environment
mapperRegistry
config
knownMappers
mappedStatements
resultMaps
sqlFragments
interceptorChain # MyBatis拦截器调用链
interceptors # 拦截器集合,记录了所有实现了Interceptor接口,并且使用了invocation变量的类
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
package org.apache.ibatis.plugin;
... public class Plugin implements InvocationHandler {
... public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
// 执行拦截器的逻辑
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
...
}
package com.github.pagehelper;
... /**
* Mybatis - 通用分页拦截器
*/
@SuppressWarnings("rawtypes")
@Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class PageHelper extends BasePageHelper implements Interceptor {
private final SqlUtil sqlUtil = new SqlUtil(); @Override
public Object intercept(Invocation invocation) throws Throwable {
// 执行 sqlUtil 的拦截逻辑
return sqlUtil.intercept(invocation);
} @Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
} @Override
public void setProperties(Properties properties) {
sqlUtil.setProperties(properties);
}
}
package com.github.pagehelper.util;
... public class SqlUtil extends BaseSqlUtil implements Constant {
... /**
* 真正的拦截器方法
*
* @param invocation
* @return
* @throws Throwable
*/
public Object intercept(Invocation invocation) throws Throwable {
try {
return doIntercept(invocation); // 执行拦截
} finally {
clearLocalPage(); // 清空 ThreadLocal<Page>
}
} /**
* 真正的拦截器方法
*
* @param invocation
* @return
* @throws Throwable
*/
public Object doIntercept(Invocation invocation) throws Throwable {
// 省略其他代码 // 调用方法判断是否需要进行分页
if (!runtimeDialect.skip(ms, parameterObject, rowBounds)) {
ResultHandler resultHandler = (ResultHandler) args[3];
// 当前的目标对象
Executor executor = (Executor) invocation.getTarget(); /**
* getBoundSql 方法执行后,boundSql 中保存的是没有 limit 的sql语句
*/
BoundSql boundSql = ms.getBoundSql(parameterObject); // 反射获取动态参数
Map<String, Object> additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql);
// 判断是否需要进行 count 查询,默认需要
if (runtimeDialect.beforeCount(ms, parameterObject, rowBounds)) {
// 省略代码 // 执行 count 查询
Object countResultList = executor.query(countMs, parameterObject, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
Long count = (Long) ((List) countResultList).get(0); // 处理查询总数,从 ThreadLocal<Page> 中取出 page 并设置 total
runtimeDialect.afterCount(count, parameterObject, rowBounds);
if (count == 0L) {
// 当查询总数为 0 时,直接返回空的结果
return runtimeDialect.afterPage(new ArrayList(), parameterObject, rowBounds);
}
}
// 判断是否需要进行分页查询
if (runtimeDialect.beforePage(ms, parameterObject, rowBounds)) {
/**
* 生成分页的缓存 key
* pageKey变量是分页参数存放的地方
*/
CacheKey pageKey = executor.createCacheKey(ms, parameterObject, rowBounds, boundSql);
/**
* 处理参数对象,会从 ThreadLocal<Page> 中将分页参数取出来,放入 pageKey 中
* 主要逻辑就是这样,代码就不再单独贴出来了,有兴趣的同学可以跟进验证
*/
parameterObject = runtimeDialect.processParameterObject(ms, parameterObject, boundSql, pageKey);
/**
* 调用方言获取分页 sql
* 该方法执行后,pageSql中保存的sql语句,被加上了 limit 语句
*/
String pageSql = runtimeDialect.getPageSql(ms, boundSql, parameterObject, rowBounds, pageKey);
BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(), pageSql, boundSql.getParameterMappings(), parameterObject);
//设置动态参数
for (String key : additionalParameters.keySet()) {
pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
/**
* 执行分页查询
*/
resultList = executor.query(ms, parameterObject, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
} else {
resultList = new ArrayList();
}
} else {
args[2] = RowBounds.DEFAULT;
// 不需要分页查询,执行原方法,不走代理
resultList = (List) invocation.proceed();
}
/**
* 主要逻辑:
* 从 ThreadLocal<Page> 中取出 page
* 将 resultList 塞进 page,并返回
*/
return runtimeDialect.afterPage(resultList, parameterObject, rowBounds);
}
...
}
知识拓展
- Oracle
- Mysql
- MariaDB
- SQLite
- Hsqldb
- PostgreSQL
- DB2
- SqlServer(2005,2008)
- Informix
- H2
- SqlServer2012
- Derby
- Phoenix
总结
参考资料
创作时间:2019-11-20 21:21
Github PageHelper 原理解析的更多相关文章
- Mybatis分页插件: pageHelper的使用及其原理解析
在实际工作中,很进行列表查询的场景,我们往往都需要做两个步骤:1. 查询所需页数对应数据:2. 统计符合条件的数据总数:而这,又会导致我们必然至少要写2个sql进行操作.这无形中增加了我们的工作量,另 ...
- 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现
本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...
- 开源磁力搜索爬虫dhtspider原理解析
开源地址:https://github.com/callmelanmao/dhtspider. 开源的dht爬虫已经有很多了,有php版本的,python版本的和nodejs版本.经过一些测试,发现还 ...
- Gulp实战和原理解析
Gulp实战和原理解析(以weui作为项目实例)http://i5ting.github.io/stuq-gulp/
- RPC原理解析
1.RPC原理解析 1.1 什么是RPC RPC(Remote Procedure Call Protocol) --远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络 ...
- ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析
ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析 上一篇:ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解 ...
- [置顶]
滴滴插件化框架VirtualAPK原理解析(一)之插件Activity管理
上周末,滴滴与360都开源了各自的插件化框架,VirtualAPK与RePlugin,作为一个插件化方面的狂热研究者,在周末就迫不及待的下载了Virtualapk框架来进行研究,本篇博客带来的是Vir ...
- android黑科技系列——应用市场省流量更新(增量升级)原理解析
一.前言 最近在看热修复相关的框架,之前我们已经看过了阿里的Dexposed和AndFix这两个框架了,不了解的同学可以点击这里进行查看:Dexposed框架原理解析 和 AndFix热修复框架原理解 ...
- MS14-068(CVE-2014-6324)域控提权利用及原理解析
漏洞利用 0x01 漏洞利用前提 1.域控没有打MS14-068的补丁(KB3011780) 2.拿下一台加入域的计算机 3.有这台域内计算机的域用户密码和Sid 0x02 工具下载 Ms14-068 ...
随机推荐
- 域渗透-凭据传递攻击(pass the hash)完全总结
总结下PtH具体形式(wmicexec.powershell.msf等) 0x01 PtH攻击原理 && 黄金白银票据 PtH攻击原理 wiki https://en.wikiped ...
- Echart:前端很好的数据图表展现工具+demo
官网: http://echarts.baidu.com/index.html 通过一个简单的小Demo介绍echart的使用:demo均亲测可以运行 demo1: 1.新建一个echarts.ht ...
- opencv::轮廓发现(find contour in your image)
轮廓发现(find contour) 轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法. 所以边缘提取的阈值选定会影响最终轮廓发现结果 //发现轮廓 cv::findContours( InputO ...
- 从C++到C++/CLI
本文转载于:https://www.cnblogs.com/feisky/archive/2009/11/22/1607999.html 刘未鹏(pongba) /文 看起来只是在C++后面多写了一个 ...
- SpringBoot学习(二)探究Springboot启动机制
引言: SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起探究一下SpringBoot的启动原 ...
- 5.分析snkrs的Android的DeviceID生产规则
分析Android的DeviceID生产 前面已经把web端的分析了一些,要想实现其实容易也难,容易是规则很容易,难是时间生成控制很难,我之前大概花了一周时间分析和梳理,然后行为测试,之前我已经讲过c ...
- Charles抓包工具的使用(一)
前提:charles的说明 Charles其实是一款代理服务器,通过过将自己设置成系统(电脑或者浏览器)的网络访问代理服务器,然后截取请求和请求结果达到分析抓包的目的.该软件是用Java写的,能够在W ...
- Java基础(三十五)Math、Random类和数字格式化(String.format方法)
一.Math类 Math类常用的方法: public static long abs (double a) 返回a的绝对值 public static double max (double a,dou ...
- WARNING: The host 'WeiLei' could not be looked up with resolveip.
[root@WeiLei data]# /usr/local/mysql/scripts/mysql_install_db --user=mysql --basedir=/usr/local/mysq ...
- POI 生成 word 文档 简单版(包括文字、表格、图片、字体样式设置等)
POI 生成word 文档 一般有两种方法: ① word模板 生成word 文档 : ② 写代码直接生成 word 文档: 我这里演示的是第二种方法,即写代码生成 word文档,不多说废话,直接 ...