Mybatis第三方PageHelper分页插件的使用与原理

目录
  • ​用法
  • ​原理
    • PageHelper.startPage做了什么
    • Page分页信息在哪使用
    • 拦截器
    • 插件
    • 拦截器链
    • 加载&调用拦截器
    • @Intercepts注解
    • 通过PageHelper创建代理对象
    • 拦截器的调用源头-动态代理
    • 分页逻辑
  • ​总结

​用法

此时commentAnalyses为Page对象(PageHelper插件包内定义的)

而Page对象继承自JDK中的ArrayList,扩展并封装了一些page相关的字段,如页码,每页大小,总记录数,总页数等。

​原理

我们就加了一行,它是如何帮助我们实现分页的呢?请往下看。

PageHelper.startPage做了什么

我们看这一行PageHelper.startPage(pageIndex, pageSize);做了什么。这个类中重载了好多个startPage方法,最终调用到如下的一个方法

​可以看到该方法将分页信息作为构造器参数实例化Page对象,调用SqlUtil.getLocalPage()获取一个旧的Page对象,最后调用SqlUtil.setLocalPage(page);把新创建的Page对象set进去。

我们看看这两个方法做了什么。如下,很简单,从ThreadLocal中获取Page对象,将Page对象set到ThreadLocal中。知道ThreadLocal作用的不用多说,不知道的可以理解为用于保存本地变量,并与线程绑定。

​看到这我们暂且将这一行的作用记为,创建并保存Page对象(分页信息)到ThreadLocal中。

Page分页信息在哪使用

那么既然保存了,就有使用的地方。

我通过代码追踪的方式定位到被调用的地方,通过回溯,发现是从这个类com.github.pagehelper.dialect.AbstractDialect发起调用的

点进去看了一下,主要是取出Page对象用于做一些判断,或保存page相关的信息。应该后续会涉及到

拦截器

上述AbstractDialect类中的这些方法再回溯,指向了

com.github.pagehelper.util.SqlUtil#doIntercept方法,intercep调用了doIntercep方法,

继续往上追踪来到了com.github.pagehelper.PageHelper#intercept,这是Interceptor接口的方法。

然后是org.apache.ibatis.plugin.Plugin#invoke调用了com.github.pagehelper.PageHelper#intercept

可以看到PageHelper实现了Interceptor,这个接口是Mybatis官方提供的,中文意思是拦截器,所以有可能是通过实现这个拦截器做了某些操作来实现分页的。

插件

通过代码追踪我们看到Interceptor的intercept方法是在Mybatis的一个org.apache.ibatis.plugin.Plugin类的invoke方法中调用的,而这个Plugin类实现了JDK的java.lang.reflect.InvocationHandler接口,这是JDK代理接口。

这个Plugin中有一个wrap方法会返回一个代理类,所以当调用这个代理类的方法时就会走到上面的invoke方法,就可能会进到拦截器的intercept方法。

所以我们看这个warp在哪调的,就知道啥时候创建这个代理类。就是在上面的PageHelper中,再贴一下代码

拦截器链

而这个PageHelper中的plugin方法是实现自Interceptor拦截器接口,所以会有一个地方统一调这个方法,往上追溯就会发现是在org.apache.ibatis.plugin.InterceptorChain拦截器链中调用的,如下。

该类有一个List保存所有拦截器,还有三个方法,分别是pluginAll用于调用所有拦截器的plugin方法,addInterceptor添加拦截器,getInterceptors获取拦截器链。

看到这大致明白了它的原理,PageHelper通过实现Mybatis的Interceptor接口实现分页,Mybatis通过InterceptorChain调用所有Interceptor。

加载&调用拦截器

那么我们看看Mybatis的拦截器是什么时候添加到拦截器链,什么时候被调用的。

通过代码追溯,发现在Configuration的addInterceptor方法中调用添加方法,Configuration.addInterceptor是在XMLConfigBuilder的pluginElement方法中被调用

​而XMLConfigBuilder是解析XML方式的Mybatis的配置的,顾名思义pluginElement方法是解析XML中plugin相关的配置节点的

而我们确实在XML中配置了plugin

所以我们现在知道了mybatis的拦截器是在Mybatis解析配置文件时,解析plugins节点时添加到InterceptorChain中的。

拦截器什么时候调用的。我们看InterceptorChain的pluginAll方法在哪调的,通过代码追踪有如下四个地方调用拦截器链

@Intercepts注解

而PageHelper这个拦截器,我们可以发现这个类上有一个@Intercepts注解,这个注解接收的值为@Signature注解,在Signature注解配置了,Executor.class,query还有四个class:MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class。

了解Mybatis的插件机制的就明白了,这一行配置的意思是拦截Executor中的query方法,方法参数列表类型是MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class,就是下面这个方法。

所以看到这,我们可以断定InterceptorChain的pluginAll方法在上述调用点的第四个,也就是org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType),可想而知newExecutor,也就是创建Executor实例,可以断定,创建Executor时通过PageHelper的plugin方法包装了Executor,返回的是Executor的代理类。下面会讲到创建动态代理。

通过PageHelper创建代理对象

我们在正向回顾一下,如何调到PageHelper的。首先进入到pluginAll

然后会调到PageHelper的plugin方法,内部又调Plugin的warp方法

我们看看Plugin.wrap方法干了啥,代码如下。代码跟过来我们知道现在的target是Executor,interceptor是PageHelper。首先获取PageHelper拦截信息,然后筛选target是否是需要拦截的类型,这里会进入if判断逻辑,返回Executor的代理对象。

所以这时创建的Executor实例是代理对象,那么就会在某个时候调用代理的invoke方法(org.apache.ibatis.plugin.Plugin#invoke),invoke调Interceptor拦截器的intercept方法,从而调PageHelper的intercept方法执行分页逻辑

org.apache.ibatis.plugin.Plugin#invoke ==》 com.github.pagehelper.PageHelper#intercept

拦截器的调用源头-动态代理

因为返回的是Executor的动态代理,所以肯定是调用Executor的某个方法时触发进到invoke方法,具体在哪调的不好找。我们通过打断点的方式看是从哪进invoke方法的,首先断点打到Plugin的invoke方法内

通过调用栈看到是org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)调过来的,在invoke方法内判断了目标方法是不是我们要拦截的方法,因为PageHelper上注解的也是拦截这个方法,所以会进入到Plugin的invoke方法的第61行。所以就会进入到PageHelper的intercept方法,执行具体的拦截逻辑。

分页逻辑

思路就是拼SQL。

通过代码跟踪,最终的分页逻辑是在com.github.pagehelper.util.SqlUtil#doIntercept方法中,第162行,获取分页SQL,

调用

com.github.pagehelper.dialect.AbstractDialect#getPageSql(org.apache.ibatis.mapping.MappedStatement, org.apache.ibatis.mapping.BoundSql, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.cache.CacheKey)

com.github.pagehelper.dialect.AbstractDialect#getPageSql(org.apache.ibatis.mapping.MappedStatement, org.apache.ibatis.mapping.BoundSql, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.cache.CacheKey)中调用com.github.pagehelper.dialect.AbstractDialect#getPageSql(java.lang.String, com.github.pagehelper.Page, org.apache.ibatis.session.RowBounds,org.apache.ibatis.cache.CacheKey),是一个抽象方法,具体实现有好多种

我们看mysql的,在原始SQL 拼接了" limit ?,?"

​总结

以上是PageHelper实现分页的原理,总结一下:

Mybatis在四个地方留了扩展点,可以通过自定义拦截器实现Interceptor接口的plugin方法,执行自定义逻辑,可以通过该方法对Executor、ParameterHandler、ResultSetHandler、StatementHandler四个对象进行增强、扩展。

PageHelper实现了Interceptor接口,它的plugin方法调用Plugin.wrap方法对目标对象进行包装,包装成一个代理对象并返回,代理类的实现就是Plugin自身。

Plugin.wrap方法判断目标对象是否需要返回代理对象,判断依据是:Interceptor实现类(这里是PageHelper)上注解标注的类是否包含目标对象所属类。这里PageHelper上注解标注参数为Executor对象,所以创建Executor对象会返回代理对象。

当调用Executor对象的方法时会进入到Plugin.invoke方法。invoke方法会判断是否需要走拦截器的intercept方法,判断方式是取拦截器上的注解标注的方法,这里PageHelper标注的为executor.query(四个参数的那个),所以调这个时才会被拦截器拦截,其余方法还用原始对象调用。

PageHelper的intercept方法调用SqlUtil的intercept方法最终调SqlUtil.doIntercept方法。在这个方法里会执行count语句,并将结果放到page对象里,然后判断需要分页,则将分页sql拼在原始sql上,然后执行。

简单来说就是通过mybatis的拦截器和插件实现的,PageHelper实现了Interceptor拦截器接口,并拦截Executor的query方法,在执行前PageHelper会在原始SQL前拼装分页相关的SQL。

PageHelper支持以下数据库的分页:Db2、Hsqldbt 、Informix、MySq、Oracle 、SqlServer2012、SqlServer

mybatis的插件Plugin是通过JDK动态代理对目标对象进行增强

到此这篇关于Mybatis第三方PageHelper分页插件使用与原理的文章就介绍到这了,更多相关Mybatis第三方PageHelper分页插件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Mybatis分页插件PageHelper的使用详解

    1.说明 如果你也在用Mybatis,建议尝试该分页插件,这个一定是最方便使用的分页插件. 该插件目前支持Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库分页. 2.使用方法 第一步:在Mybatis配置xml中配置拦截器插件: <plugins> <!-- com.github.pagehelper为PageHelper类所在包名 --> <plugin interceptor="com.github.pageh

  • mybatis插件pageHelper实现分页效果

    最近做的一个项目在持久层我们采用的是Mybatis今天完成了商品列表的分页查询的功能,这篇博客我分享一下如何采用pageHelper的插件实现分页.mybatis的应用,最大的好处就在于我们可以更加方便灵活的编写我们的sql语句,实现对单表或者多表的增删改查,在这基础上我们使用pageHelper插件实现分页更加方便了我们对项目的开发,提高了开发效率,我们以实现商品列表的查询为背景,详细介绍一下如何应用这个插件简单的实现分页功能. 1.jar包引入 我们项目中在依赖管理方面采用的是Maven,所

  • Mybatis分页插件PageHelper的配置和简单使用方法(推荐)

    前言 在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页. 前端分页 一次性请求数据表格中的所有记录(ajax),然后在前端缓存并且计算count和分页逻辑,一般前端组件(例如dataTable)会提供分页动作. 特点是:简单,很适合小规模的web平台:当数据量大的时候会产生性能问题,在查询和网络传输的时间会很长. 后端分页 在ajax请求中指定页码(pageNum)和每页的大小(pageSize),后端查询出当页的数据返回

  • mybatis分页插件pageHelper详解及简单实例

    mybatis分页插件pageHelper详解及简单实例 工作的框架spring springmvc mybatis3 首先使用分页插件必须先引入maven依赖,在pom.xml中添加如下 <!-- 分页助手 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5

  • 使用mybatis插件PageHelper实现分页效果

    最近都在忙着写一个网站项目,今天做一个分页功能的时候,遇到了分页效果实现不了的问题,查了好久的资料,后来终于是成功解决啦,记录一下~ 1.在pom.xml中添加分页插件依赖 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>4.1.5</version> </depend

  • Mybatis分页插件PageHelper配置及使用方法详解

    环境 框架:spring+springmvc+mybatis pom.xml <!-- 引入mybatis的 pagehelper 分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.2</version> </dependency&g

  • MyBatis分页插件PageHelper的具体使用

    MyBatis分页插件PageHelper 如果你也在用 MyBatis,建议尝试该分页插件,这一定是最方便使用的分页插件.分页插件支持任何复杂的单表.多表分页. PageHelper是一个Mybatis的分页插件, 负责将已经写好的sql语句, 进行分页加工. PageHelper的使用 优点:无需你自己去封装以及关心sql分页等问题,使用很方便,前端取数据也很方便. 1.引入pagehelper依赖 <dependency> <groupId>com.github.pagehe

  • MyBatis基于pagehelper实现分页原理及代码实例

    使用pagehelper分页的原理是: 通过MyBatis的插件原理(类似web里的filter拦截器),在mapper配置文件将pagehelper注册为MyBatis的插件,从而进行分页 1.通过maven引入pagehelper依赖: <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --> <dependency> <groupId>com.github.pagehe

  • Mybatis第三方PageHelper分页插件的使用与原理

    目录 ​用法 ​原理 PageHelper.startPage做了什么 Page分页信息在哪使用 拦截器 插件 拦截器链 加载&调用拦截器 @Intercepts注解 通过PageHelper创建代理对象 拦截器的调用源头-动态代理 分页逻辑 ​总结 ​用法 ​ 此时commentAnalyses为Page对象(PageHelper插件包内定义的) ​ 而Page对象继承自JDK中的ArrayList,扩展并封装了一些page相关的字段,如页码,每页大小,总记录数,总页数等. ​原理 我们就加了

  • Mybatis pagehelper分页插件使用过程解析

    这篇文章主要介绍了mybatis pagehelper分页插件使用过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用过mybatis的人都知道,mybatis本身就很小且简单,sql写在xml里,统一管理和优化.缺点当然也有,比如我们使用过程中,要使用到分页,如果用最原始的方式的话,1.查询分页数据,2.获取分页长度,也就是说要使用到两个方法才能完成分页.有没有更更好的分页方式的,pagehelper分页插件因此而诞生,他的原理是利用

  • Mybatis图文并茂讲解分页插件

    目录 1. Mybatis分页插件 1.1 分页插件介绍 1.2 分页插件的使用 1.3 分页插件的参数获取 1.4 分页插件知识小结 1. Mybatis分页插件 1.1 分页插件介绍 分页可以将很多条结果进行分页显示. 如果当前在第一页,则没有上一页.如果当前在最后一页,则没有下一页. 需要明确当前是第几页,这一页中显示多少条结果. MyBatis分页插件总结 在企业级开发中,分页也是一种常见的技术.而目前使用的 MyBatis 是不带分页功能的,如果想实现分页的 功能,需要我们手动编写 L

  • 使用Mybatis的PageHelper分页工具的教程详解

    1.导入相关的jar包 在pom.xm中加入 <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.10</ver

  • SpringBoot+Mybatis分页插件PageHelper实现分页效果

    目录 一.项目结构 二.插件引入 三.代码 四.测试: 最近刚入职新公司,项目是从零开始搭建的项目.我觉得是时候考验是驴还是千里马的时候.都是泪就不多说了. 附上一篇Mybatis常用的分页案例.这次要做的是最常见的分页效果,也是基础功能.但是很多人都做不好的.这次采用Mybatis分页插件PageHelper.   仅献给伸手党的大爷们.大爷们好!拿代码记得扣666!!小的在这给感谢了!! 一.项目结构 按照controller,service,mapper(也叫dao)来建立项目,utils

  • SpringBoot+Mybatis+Druid+PageHelper实现多数据源并分页方法

    前言 本篇文章主要讲述的是SpringBoot整合Mybatis.Druid和PageHelper 并实现多数据源和分页.其中SpringBoot整合Mybatis这块,在之前的的一篇文章中已经讲述了,这里就不过多说明了.重点是讲述在多数据源下的如何配置使用Druid和PageHelper. Druid介绍和使用 在使用Druid之前,先来简单的了解下Druid. Druid是一个数据库连接池.Druid可以说是目前最好的数据库连接池!因其优秀的功能.性能和扩展性方面,深受开发人员的青睐. Dr

  • Spring Boot+Mybatis+Druid+PageHelper实现多数据源并分页的方法

    前言 本篇文章主要讲述的是SpringBoot整合Mybatis.Druid和PageHelper 并实现多数据源和分页.其中SpringBoot整合Mybatis这块,在之前的的一篇文章中已经讲述了,这里就不过多说明了.重点是讲述在多数据源下的如何配置使用Druid和PageHelper . Druid介绍和使用 在使用Druid之前,先来简单的了解下Druid. Druid是一个数据库连接池.Druid可以说是目前最好的数据库连接池!因其优秀的功能.性能和扩展性方面,深受开发人员的青睐. D

  • Mybatis Plus整合PageHelper分页的实现示例

    Mapper Plus自带分页PaginationInterceptor对象,虽然说目前没有什么问题,并且使用简单,但是个人感觉有个弊端:目前个人使用中,想要用Mapper Plus自带的分页功能的话需要在mapper对象中传入一个Page对象才可以实现分页,这样耦合度是不是太高了一点,从web到service到mapper,这个Page对象一直都在传入,这样的使用让人感觉有点麻烦,但是Mapper Plus不得不说真的是很好用的. PageHelper用过的人多多少少了解,这个框架要实现分页只

随机推荐