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 ...
随机推荐
- 代码审计-MetInfo 6.0.0 sql注入漏洞
首先漏洞存在于app\system\message\web\message.class.php文件中,变量{$_M[form][id]} 直接拼接在SQL语句中,且验证码检测函数是在SQL语句查询之后 ...
- PowerUp攻击渗透实战
记录下PowerUp在实战渗透中的利用 准备环境: kali linux 攻击机 已获得靶机meterpreter(非管理)权限 win7 靶机 拥有powershell环境 1)Invoke-Al ...
- ndnsim结果分析
ndnSIM中提供了几种trace用来对仿真结果进行跟踪,生成数据文件txt 官网地址: https://ndnsim.net/current/metric.html#packet-trace-hel ...
- Dell R720 RAID配置
Dell服务器上一般都带有Raid卡,Raid5配置请看下边,亲们 1. 将服务器接上电源,显示器,键盘,并开机 2. 按 ctrl + R进入Raid设置 3. 将光标放置在Raid卡那,按F2,选 ...
- Uipath 获取当前浏览器页面URL
文章来源东京IT青年前线 http://www.rpatokyo.com Uipath 获取当前浏览器页面URL的方法 Inject Js Script 因为目前没有直接获取页面URL的Act ...
- 百万年薪python之路 -- 并发编程之 多线程 三
1. 阻塞,非阻塞,同步,异步 进程运行的三个状态: 运行,就绪,阻塞. 从执行的角度: 阻塞: 进程运行时,遇到IO了,进程挂起,CPU被切走. 非阻塞: 进程没有遇到IO 当进程遇到IO, ...
- WPF_在APP.xaml应用资源样式
先上代码: <Application x:Class="NetAnalysis.App" xmlns="http://schemas.microsoft.com/w ...
- Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置
用过 Spring Boot 的小伙伴都知道,我们只需要在项目中引入 spring-boot-starter-web 依赖,SpringMVC 的一整套东西就会自动给我们配置好,但是,真实的项目环境比 ...
- 搭建 webpack + react 框架爬坑之路
由于工程实践需要搭一个 webpack + react 框架,本人刚开始学,就照b站上的react黑马视频做,爬过无数个坑...希望读者能引以为戒.我的是macos系统 https://www.bil ...
- (七)javac编译
文章目录 1.基本格式 2.目标路径 2.1 缺省项 2.2 指定路径 2.2.1 全路径 2.2.2 相对路径 3.源文件 3.1 无第三方库 3.1.1 基本方法 3.1.2 添加目录 3.1.3 ...