MyBatis分页插件PageHelper的使用及原理浅析

分页是我们开发过程中经常遇到的一个需求功能点,以前做分页功能的时候,用常规的写法总觉得不够方便,虽然看起来似乎比较简单,但总觉得不够灵活,并且代码具体一定的侵入性,一直想找个方法解决这个烦恼。最近在学习Spring Boot的过程中发现了一个MyBatis分页插件PageHelper,使用之后感觉还可以,于是就深入研究了一番,下面做一个简单的使用和原理浅析。

一、PageHelper插件的基本使用方法

本文是基于Spring Boot框架来讲述PageHelper插件的使用,在其他框架下使用会有些配置差异,但知道大致原理后使用起来大同小异,在此就不多做赘述了。

第一步:pom.xml中引入插件的依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.1.0</version>
</dependency>

当然也要引入mysql数据库驱动包和spring boot集成mybatis依赖包

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
 </dependency>

二步:注册Mybatis插件PageHelper
创建一个配置类PageHelperConfiguration,通过@Bean注入PageHelper类,代码如下:

import com.github.pagehelper.PageHelper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

@Configuration
public class PageHelperConfiguration {

    @Bean
    public PageHelper pageHelper() {
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("offsetAsPageNum", "true");
        properties.setProperty("rowBoundsWithCount", "true");
        properties.setProperty("reasonable", "true");
        pageHelper.setProperties(properties);
        return pageHelper;
    }
}

第三步:通过调用PageHelper的startPage方法实现分页查询效果
如下图示例:
在这里插入图片描述
说明:以上为了方便演示,参数中直接写死了数字,实际中应该使用变量代替。

如上图示例,PageHelper.startPage(1, 2)语句配置了查询第1页数据,每页为2条数据,执行此条语句再执行bookMapper.selectAll()查询语句结果只查询出两条记录,达到分页的效果。
注意:PageHelper.startPage(1, 2)这条语句不一定非得放到这里,放到此方法外也是可以的,只要保证在同一个线程下分页查询之前调用一次PageHelper.startPage(1, 2)即可。

以上效果是经过亲测的,到这里你可能会问,这是什么原因呢?下面我们就简单浅析下其中的原理,以解决我们的疑惑。

二、PageHelper插件的原理浅析

1. 首先来看下PageHelper.startPage(1, 2)这行代码做了什么事情,dubug查看源代码:
在这里插入图片描述
PageHelper类中有几个重载方法startPage,最后进入到上图中的方法,通过源码可以看到,这个方法主要是把pageNum和pageSize封装到page对象中。同时,更重要的一点,这里通过SqlUtil类中的setLocalPage(page)方法将page对象放到了ThreadLocal中,为了隔离不同线程之间的page对象,使线程之间互不影响。后面的查询操作也会通过SqlUtil类获取当前线程下的page对象,利用当前线程下配置的page对象来进行sql的分页查询。
在这里插入图片描述
上图可以看到SqlUtil类中定义了LOCAL_PAGE变量,用来存储每个线程配置的page对象(分页参数)。

2. 接着我们来看下bookMapper.selectAll()这条语句的执行逻辑,继续debug发现,在查询之前会调用PageHelper的intercept方法,从源码很明显可以看到,因为PageHelper实现了MyBatis的拦截器接口Interceptor,并实现了intercept方法。如下图:
在这里插入图片描述
在这里插入图片描述
3. 接着往下debug,这里会调用SqlUtil的processPage方法,processPage方法再调用本类中的_processPage方法,_processPage最后再调用本类的doProcessPage方法,doProcessPage返回最终结果。部分源码:
在这里插入图片描述
上图中,doProcessPage方法中的page即为前面PageHelper.startPage(1, 2)执行过程中设置到ThreadLocal中的page对象。
因此,代码走到这里,我们就大概知道这两行代码之间的关联关系了。

我们可能还有一个疑问,我们知道PageHelperConfiguration类通过@Bean把PageHelper注入到容器了,但设置的那几个属性有什么作用呢?

4.debug启动服务发现,其实这几个属性配置最终是通过SqlUtil的setProperties(Properties p)方法设置到SqlUtil中对应的属性中,SqlUtil中的部分源码如下:
在这里插入图片描述
在这里插入图片描述
简单来说,PageHelper中setProperties(properties)方法主要是暴露出来给我们配置分页的一些简单策略。

5.(重要)最后补充:每个线程在执行一次PageHelper.startPage(xx, xx)之后只能执行一次分页查询,因为每次分页查询后会把当前线程中的page对象清除掉。所以,如果要在同一个线程中做多个分页查询,那么在每个分页查询之前执行对应的PageHelper.startPage(xx, xx)。分页查询后清除掉当前线程中的page对象,如源码:
在这里插入图片描述
在这里插入图片描述

三、总结

本文主要是想介绍PageHelper的基本使用及其原理分析,其中还有一些细节并没有做过多的赘述,比如:
1、PageHelper支持多种数据库的分页。
2、PageHelper中有6个startPage重载方法以及3个offsetPage重载方法,他们的使用基本比较相似。
3、PageHelper中有两个orderBy重载方法,支持排序分页。

整体来说,插件还是挺好的。不过还是要瞎吐个小槽,发现PageHelper有几个小缺点(纯属个人理解,仅供参考,如有不对,敬请指教):
1、Properties中的属性配置如properties.setProperty(“offsetAsPageNum”, “true”)使用起来不太方便,如果把属性的key定义一组变量集合,把value定义成一个枚举类,那么用户配置的时候会更加友好也更加容易上手。
2、PageHelper中定义了Properties属性,先存储用户配置的参数,最后又把Properties中的属性一个一个设置到SqlUtil中的属性中,如果直接把配置值设置到SqlUtil中可能会更加方便。
3、PageHelper中setProperties(properties)的提供的几个参数配置比较费解,当然,我暂时也没想到改进方法。

附:PageHelper是一款开源的Mybatis分页插件,github地址:https://github.com/pagehelper/Mybatis-PageHelper

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值