MyBatis 分页插件 PageHelper
最佳答案 问答题库458位专家为你答疑解惑
文章目录
- 前言
- PageHelper 应用
- 实现原理剖析
- 应用场景分析
前言
分页插件PageHelper是我们使用Mybatis到的比较多的插件应用,适用于任何复杂的单表、多表分页查询操作。本文介绍PageHelper的使用及原理。
PageHelper 应用
添加依赖
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>4.1.6</version>
</dependency>
在全局配置文件中注册
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper"><property name="dialect" value="mysql" /><!-- 该参数默认为false --><!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 --><!-- 和startPage中的pageNum效果一样 --><property name="offsetAsPageNum" value="true" /><!-- 该参数默认为false --><!-- 设置为true时,使用RowBounds分页会进行count查询 --><property name="rowBoundsWithCount" value="true" /><!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 --><!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型) --><property name="pageSizeZero" value="true" /><!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 --><!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 --><!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 --><property name="reasonable" value="false" /><!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 --><!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 --><!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 --><!-- 不理解该含义的前提下,不要随便复制该配置 --><property name="params" value="pageNum=start;pageSize=limit;" /><!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page --><property name="returnPageInfo" value="check" />
</plugin>
在执行查询操作之前设置了一句PageHelper.startPage(1,5);
即可实现分页效果。
实现原理剖析
PageHelper也必然要实现 Interceptor ,通过源码我们可以发现是PageHelper方法头部添加的注解,声明了该拦截器拦截的是Executor的query方法。
执行查询操作的时候, Executor.query() 方法的执行本质上是执行 Executor的代理对象的方法。先来看下Plugin中的invoke方法
/*** 代理对象方法被调用时执行的代码* @param proxy* @param method* @param args* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 获取当前方法所在类或接口中,可被当前Interceptor拦截的方法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);}}
interceptor.intercept(new Invocation(target, method, args));方法的执行会进入到 PageHelper的intercept方法中
/*** Mybatis拦截器方法** @param invocation 拦截器入参* @return 返回执行结果* @throws Throwable 抛出异常*/public Object intercept(Invocation invocation) throws Throwable {if (autoRuntimeDialect) {SqlUtil sqlUtil = getSqlUtil(invocation);return sqlUtil.processPage(invocation);} else {if (autoDialect) {initSqlUtil(invocation);}return sqlUtil.processPage(invocation);}}
intercept方法
/*** Mybatis拦截器方法** @param invocation 拦截器入参* @return 返回执行结果* @throws Throwable 抛出异常*/public Object intercept(Invocation invocation) throws Throwable {if (autoRuntimeDialect) { // 多数据源SqlUtil sqlUtil = getSqlUtil(invocation);return sqlUtil.processPage(invocation);} else { // 单数据源if (autoDialect) {initSqlUtil(invocation);}return sqlUtil.processPage(invocation);}}
在interceptor方法中首先会获取一个SqlUtils对象
SqlUtil:数据库类型专用sql工具类,一个数据库url对应一个SqlUtil实例,SqlUtil内有一个Parser对象,如果是mysql,它是MysqlParser,如果是oracle,它是OracleParser,这个Parser对象是SqlUtil不同实例的主要存在价值。执行count查询、设置Parser对象、执行分页查询、保存Page分页对象等功能,均由SqlUtil来完成。
initSqlUtil 方法创建的解析器
public static Parser newParser(Dialect dialect) {Parser parser = null;switch (dialect) {case mysql:case mariadb:case sqlite:parser = new MysqlParser();break;case oracle:parser = new OracleParser();break;case hsqldb:parser = new HsqldbParser();break;case sqlserver:parser = new SqlServerParser();break;case sqlserver2012:parser = new SqlServer2012Dialect();break;case db2:parser = new Db2Parser();break;case postgresql:parser = new PostgreSQLParser();break;case informix:parser = new InformixParser();break;case h2:parser = new H2Parser();break;default:throw new RuntimeException("分页插件" + dialect + "方言错误!");}return parser;}
不同的数据库方言,创建了对应的解析器。
sqlUtil.processPage(invocation);方法中会完成分页SQL的绑定
进入 PageSqlSource 的getBoundSql方法中
/*** 获取BoundSql** @param parameterObject* @return*/@Overridepublic BoundSql getBoundSql(Object parameterObject) {Boolean count = getCount();if (count == null) {return getDefaultBoundSql(parameterObject);} else if (count) {return getCountBoundSql(parameterObject);} else {return getPageBoundSql(parameterObject);}}
getPageBoundSql获取分页的SQL语句,在这个方法中可以发现查询总的记录数的SQL生成
@Overrideprotected BoundSql getPageBoundSql(Object parameterObject) {String tempSql = sql;String orderBy = PageHelper.getOrderBy();if (orderBy != null) {tempSql = OrderByParser.converToOrderBySql(sql, orderBy);}tempSql = localParser.get().getPageSql(tempSql);return new BoundSql(configuration, tempSql, localParser.get().getPageParameterMapping(configuration, original.getBoundSql(parameterObject)), parameterObject);}
最终在这个方法中生成了对应数据库的分页语句
应用场景分析
99%的人还看了
相似问题
- Kotlin学习——kt里的集合,Map的各种方法之String篇
- Office文件在线预览大全-Word文档在线预览的实现方法-OFD文档在线预览-WPS文件在线预览
- composer切换全局镜像源的方法
- Python通过selenium调用IE11浏览器报错解决方法
- 测试用例的设计方法(全):正交实验设计方法|功能图分析方法|场景设计方发
- Java8新特性 ----- Lambda表达式和方法引用/构造器引用详解
- C#中抽象类、抽象方法和接口暨内联临时变量的精彩表达
- ChatGLM2 大模型微调过程中遇到的一些坑及解决方法(更新中)
- 类方法,静态方法和实例方法的区别及应用场景
- 【链表的说明、方法---顺序表与链表的区别】
猜你感兴趣
版权申明
本文"MyBatis 分页插件 PageHelper":http://eshow365.cn/6-10770-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!