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 ...
随机推荐
- [CODEVS1537] 血色先锋队 - BFS
题目描述 Description 巫妖王的天灾军团终于卷土重来,血色十字军组织了一支先锋军前往诺森德大陆对抗天灾军团,以及一切沾有亡灵气息的生物.孤立于联盟和部落的血色先锋军很快就遭到了天灾军团的重重 ...
- 1、Struts2基本入门
一.了解了这几个主要的优点,会促使你考虑使用Struts2 : 1.POJO表单及POJO操作 - Struts2 去除掉了Struts框架中的Action Forms部分.在Struts2框架下,你 ...
- 实用---eclipse中的代码提示功能
Eclipse 的代码提示功能,具体配置 1. 打开Eclipse ,然后“window”→“Preferences” 2. 选择“java”,展开,“Editor”,选择“Content Assis ...
- python类的__repr__方法
python3中的类默认是新式类(继承object类). __repr__()是 Python 类中的一个特殊方法,由于 object 类己提供了该方法, 而所有 的 Python 类都是 objec ...
- find命令面试题
注意 (1)建议先创建快照 (2)有可能存在命令正确,但是查找不到文件的情况,是因为不存在相关条件的文件 (3)如果存在命令正确,但是查找不到文件的情况,则先创建相关的文件.目录.用户.组,设置好对应 ...
- spring在IoC容器中装配Bean详解
1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean ...
- EXC_BAD_ACCESS的本质详解以及僵尸模式调试原理
原文:What Is EXC_BAD_ACCESS and How to Debug It 有时候,你会遇到由EXC_BAD_ACCESS造成的崩溃. 这篇文章会告诉你什么是EXC_BAD_ACCES ...
- 百万年薪python之路 -- while循环
day02 1.while循环 -- while关键字 while 空格 条件 冒号 缩进 循环体 while 5>4: print("Hello World!") 数字中非 ...
- Java并发编程之线程池的使用
1. 为什么要使用多线程? 随着科技的进步,现在的电脑及服务器的处理器数量都比较多,以后可能会越来越多,比如我的工作电脑的处理器有8个,怎么查看呢? 计算机右键--属性--设备管理器,打开属性窗口,然 ...
- Java基础(二十六)Java IO(3)字节流(Byte Stream)
字节流是以字节为单位来处理数据的,由于字节流不会对数据进行任何转换,因此用来处理二进制的数据. 一.InputStream类与OutputStream类 1.InputStream类是所有字节输入流的 ...