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 ...
随机推荐
- Redis 3.0中文版学习(一)
网址:http://wiki.jikexueyuan.com/project/redis-guide/entry-to-master-a.html http://www.yiibai.com/redi ...
- 如何巧妙地在基于 TCP Socket 的应用中实现用户注册功能?
通常,在基于TCP的应用中(比如我开源的GGTalk即时通信系统),当TCP连接建立之后,第一个请求就是登录请求,只有登录成功以后,服务器才会允许客户端进行其它性质的业务请求.但是,注册用户这个功能比 ...
- opencv::凸包-Convex Hull
概念介绍 什么是凸包(Convex Hull),在一个多变形边缘或者内部任意两个点的连线都包含在多边形边界或者内部. 正式定义:包含点集合S中所有点的最小凸多边形称为凸包 Graham扫描算法 首先选 ...
- win10下安装openssl
1.下载运行工具:ActivePerl-5.22.1.2201-MSWin32-x64-299574.msi, 安装. 2.执行:perl example.pl,若显示“Hello from Acti ...
- java学习5-面向对象(下)
final修饰符: final用于修饰类.变量和方法. final修饰变量时,一旦获得了初始值就不可改变 1.抽象方法和抽象类 抽象方法与抽象类的规则: a.抽象方法和抽象类必须使用abstract修 ...
- 使用FastReport报表工具生成标签打印文档
在我们实际开发报表的时候,我们需要按一定的业务规则组织好报表的模板设计,让报表尽可能的贴近实际的需求,在之前的随笔中<使用FastReport报表工具生成报表PDF文档>介绍了FastRe ...
- sublime设置 reindent 快捷键
设置快捷键 使用快捷键 cmd + shift + p 打开控制面板 输入 key 关键词 点击进入 Key Bindings -User 添加如下代码 { "keys": [&q ...
- 搭建ASP.NET Core WebApi项目
步骤 从“文件”菜单中选择“新建”>“项目” . 选择“ASP.NET Core Web 应用程序”模板,再单击“下一步” . 将项目命名为 NetCoreWebApi,然后单击“创建” . 选 ...
- fenby C语言 P25
二维数组 #include <stdio.h> int main(){ int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int sum=0, ...
- GO基础之List
一.List定义 概述1.list是一种非连续存储的容器,由多个节点组成,节点通过一些变量记录彼此之间的关系.list有多种实现方法,如单向链表.双向链表等.2.Go语言中list的实现原理是双向链表 ...