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 ...
随机推荐
- [CODEVS3366] 矿石
题目描述 Description 经历了千辛万苦,小J找到了N块矿石.这些矿石都有毒性,但只要将两块矿石放在一起,再分开即可解毒.但任一两块矿石都可以互相吸引.为了降低吸引力,小J将他们放入一个直径仅 ...
- sql优化提速整理
sql优化提速整理 场景描述 在我们实际开发中,随着业务的不断增加,数据量也在不断的攀升,这样就离不开一个问题:数据查询效率优化 根据自己的以往实际项目工作经验和学习所知,现在对SQL查询优化做一个简 ...
- 《利用Python进行数据分析·第2版》第四章 Numpy基础:数组和矢量计算
<利用Python进行数据分析·第2版>第四章 Numpy基础:数组和矢量计算 numpy高效处理大数组的数据原因: numpy是在一个连续的内存块中存储数据,独立于其他python内置对 ...
- springboot 打jar包时分离配置文件
修改pom.xml文件 <build> <resources> <resource> <directory>src/main/resources< ...
- Java网络编程(二)IP、URL和HTTP
一.IP InetAddress类有一些静态工厂方法,可以连接到DNS服务器来解析主机名. 示例1:InetAddress address = InetAddress.getByName(" ...
- [AI开发]零数学公式告诉你什么是(卷积)神经网络
大部分介绍神经网络的文章中概念性的东西太多,而且夹杂着很多数学公式,读起来让人头疼,尤其没什么基础的人完全get不到作者想要表达的思想.本篇文章尝试零公式(但有少量数学知识)说清楚什么是神经网络,并且 ...
- python dict(字典)
补充知识点1: 数据类型的划分:可变数据类型.不可变数据类型 可变数据类型: 元组,bool,int,str --可哈希 不可变数据类型: list,dict,set ...
- Web for pentester_writeup之XSS篇
Web for pentester_writeup之XSS篇 XSS(跨站脚本攻击) Example 1 反射性跨站脚本,URL中name字段直接在网页中显示,修改name字段, Payload: h ...
- [2018-01-12] jquery获取当前元素的兄弟元素
$('#id').siblings() 当前元素所有的兄弟节点$('#id').prev() 当前元素前一个兄弟节点$('#id').prevaAll() 当前元素之前所有的兄弟节点$('#id'). ...
- NOIP模拟 26
我辛辛苦苦敲了3.5h的码 得分和一份8B的码一样多 自闭555. T1 marshland 最小费用可行流, 板子不会,建图更不会. 当然是用暴搜得到0分的好成绩啦 补了补板子. %了%RNB. 学 ...