Mybatis分页插件PageHelper

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 在实际的项目开发中,常常需要使用到分页,分页方式分为两种:前端分页和后端分页。前端分页:一次ajax请求数据的所有记录,然后在前端缓存并且计算count和分页逻辑,一般前端组件(例如dataTable)会提供分页动作。

在实际的项目开发中,常常需要使用到分页,分页方式分为两种:前端分页和后端分页。
前端分页:一次ajax请求数据的所有记录,然后在前端缓存并且计算count和分页逻辑,一般前端组件(例如dataTable)会提供分页动作。
特点是:简单,很适合小规模的web平台;当数据量大的时候会产生性能问题,在查询和网络传输的时间会很长。

后端分页
在ajax请求中指定页码pageNum和每页的大小pageSize,后端查询出当页的数据返回,前端只负责渲染。
特点是:复杂一些;性能瓶颈在MySQL的查询性能,这个当然可以调优解决。一般来说,开发使用的是这种方式。

不使用分页插件的分页操作

在没有使用分页插件的时候需要先写一个查询countselect语句,然后再写一个真正分页查询的语句,MySQL中有对分页的支持,是通过limit子句
limit关键字的用法是:LIMIT [offset,] rows
offset是相对于首行的偏移量(首行是0),rows是返回条数。

例如:
每页5条记录,取第一页,返回的是前5条记录
select * from tableA limit 0,5;
每页5条记录,取第二页,返回的是第6条记录,到第10条记录,
select * from tableA limit 5,5;
不过当偏移量逐渐增大的时候,查询速度可能就会变慢,性能会有所下降。

使用Mybatis分页插件PageHelper

PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件
PageHelper的github地址:https://github.com/pagehelper/Mybatis-PageHelper

在springboot中使用PageHelper

首先要在pom.xml中配置PageHelper的依赖
http://www.mvnrepository.com/中可以发现pagehelper有4.x和5.x两个版本,用法有所不同,并不是向下兼容,在使用5.x版本的时候可能会报错

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

在Mybatis的配置文件中配置PageHelper插件
假如不配置在后面使用PageInfo类时就会出现问题,输出结果的PageInfo属性值基本上都是错的
配置如下

<plugins>
        <!-- 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="false"/>
            <!-- 该参数默认为false -->
            <!-- 设置为true时,使用RowBounds分页会进行count查询 -->
            <property name="rowBoundsWithCount" value="false"/>
            <!-- 设置为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="true"/>
            <!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
            <!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
            <!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 -->
            <!--<property name="params" value="pageNum=start;pageSize=limit;pageSizeZero=zero;reasonable=heli;count=contsql"/>-->
        </plugin>
    </plugins>

上面是PageHelper官方给的配置和注释,虽然写的很多,不过确实描述的很明白。

dialect:标识是哪一种数据库,设计上必须。
offsetAsPageNum:将RowBounds第一个参数offset当成pageNum页码使用
rowBoundsWithCount:设置为true时,使用RowBounds分页会进行count查询
reasonablevalue=true时,pageNum小于1会查询第一页,如果pageNum大于pageSize会查询最后一页
注:上面的配置只针对于pagehelper4.x版本的,如果你用的是pagehelper5.x版本就要这样配置

官方推荐

1. 在 MyBatis 配置 xml 中配置拦截器插件

<!-- 
    plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
    properties?, settings?, 
    typeAliases?, typeHandlers?, 
    objectFactory?,objectWrapperFactory?, 
    plugins?, 
    environments?, databaseIdProvider?, mappers?
-->
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
        <property name="param1" value="value1"/>
    </plugin>
</plugins>

2. 在 Spring 配置文件中配置拦截器插件

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <!-- 注意其他配置 -->
  <property name="plugins">
    <array>
      <bean class="com.github.pagehelper.PageInterceptor">
        <property name="properties">
          <!--使用下面的方式配置参数,一行配置一个 -->
          <value>
            params=value1
          </value>
        </property>
      </bean>
    </array>
  </property>
</bean>

个人推荐使用第一种,配置如下

<!-- 配置分页插件 -->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->
            <property name="helperDialect" value="mysql"/>
        </plugin>
    </plugins>

如果4.x的版本用了5.x的版本报错信息如下
springboot在启动项目的时候就会报错,报错信息有很多,主要是因为

Caused by: org.apache.ibatis.builder.BuilderException: 
Error resolving class. Cause: org.apache.ibatis.type.TypeException: 
Could not resolve type alias 'com.github.pagehelper.PageInterceptor'.
Caused by: org.apache.ibatis.type.TypeException: 
Could not resolve type alias 'com.github.pagehelper.PageInterceptor'.
Caused by: java.lang.ClassNotFoundException: 
Cannot find class: com.github.pagehelper.PageInterceptor

总的来说就是缺少了com.github.pagehelper.PageInterceptor,这个是新版拦截器,5.x版本才开始使用,所以在4.x版本这样配置是不行的

那么5.x版本的配置在pagehelper4.x上能生效吗?答案是不行
报错信息如下

Caused by: org.apache.ibatis.builder.BuilderException: 
Error parsing SQL Mapper Configuration. Cause: 
java.lang.ClassCastException: com.github.pagehelper.PageHelper 
cannot be cast to org.apache.ibatis.plugin.Interceptor
Caused by: java.lang.ClassCastException: 
com.github.pagehelper.PageHelper 
cannot be cast to org.apache.ibatis.plugin.Interceptor

新版的拦截器PageInterceptor不能和旧版拦截器相互转换,所以还是不行的。

总的来说,pagehelper4.x就该用4.x的配置,pagehelper5.x就用5.x的配置(官方推荐)

项目中使用方法和结果

在配置完mybatis后,我简单的说下pagehelper的业务用法,就以分页查询用户列表为例
添加查询所以用户的mapper接口,对应的sql语句我就不写了

List<UserVo> listUser();

重点来了,然后在service中,先开启分页,然后把查询结果集放入PageInfo

public PageInfo listUserByPage(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<UserVo> userVoList=userMapper.listUser();
        PageInfo pageInfo=new PageInfo(userVoList);
        return pageInfo;
    }

PageHelper.startPage(pageNum, pageSize);这句非常重要,这段代码表示分页的开始,意思是从第pageNum页开始,每页显示pageSize条记录。
PageInfo这个类是插件里的类,这个类里面的属性会在输出结果中显示,
使用PageInfo这个类,你需要将查询出来的list放进去:

PageHelper输出的数据结构

然后在controller层调用该方法设置对应的pageNumpageSize就可以了,我设置pageNum为1, pageSize为5,看个输出结果吧

{
    "msg": "获取第1页用户信息成功",
    "code": 200,
    "data": {
        "pageNum": 1,
        "pageSize": 5,
        "size": 5,
        "orderBy": null,
        "startRow": 1,
        "endRow": 5,
        "total": 11,
        "pages": 3,
        "list": [
            {
                "userId": "a24d0c3b-2786-11e8-9835-e4f89cdc0d1f",
                "username": "2015081040"
            },
            {
                "userId": "b0bc9e45-2786-11e8-9835-e4f89cdc0d1f",
                "username": "2015081041"
            },
            {
                "userId": "b44fd6ac-2786-11e8-9835-e4f89cdc0d1f",
                "username": "2015081042"
            },
            {
                "userId": "b7ac58f7-2786-11e8-9835-e4f89cdc0d1f",
                "username": "2015081043"
            },
            {
                "userId": "bbdeb5d8-2786-11e8-9835-e4f89cdc0d1f",
                "username": "2015081044"
            }
        ],
        "prePage": 0,
        "nextPage": 2,
        "isFirstPage": true,
        "isLastPage": false,
        "hasPreviousPage": false,
        "hasNextPage": true,
        "navigatePages": 8,
        "navigatepageNums": [
            1,
            2,
            3
        ],
        "navigateFirstPage": 1,
        "navigateLastPage": 3,
        "firstPage": 1,
        "lastPage": 3
    },
    "success": true,
    "error": null
}

PageInfo这个类里面的属性:
pageNum当前页
pageSize每页的数量
size当前页的数量
orderBy排序
startRow当前页面第一个元素在数据库中的行号
endRow当前页面最后一个元素在数据库中的行号
total总记录数(在这里也就是查询到的用户总数)
pages总页数 (这个页数也很好算,每页5条,总共有11条,需要3页才可以显示完)
list结果集
prePage前一页
nextPage下一页
isFirstPage是否为第一页
isLastPage是否为最后一页
hasPreviousPage是否有前一页
hasNextPage是否有下一页
navigatePages导航页码数
navigatepageNums所有导航页号
navigateFirstPage导航第一页
navigateLastPage导航最后一页
firstPage第一页
lastPage最后一页

安全性

PageHelper 安全调用

1. 使用 RowBoundsPageRowBounds参数方式是极其安全的
2. 使用参数方式是极其安全的
3. 使用 ISelect 接口调用是极其安全的
ISelect接口方式除了可以保证安全外,还特别实现了将查询转换为单纯的 count 查询方式,这个方法可以将任意的查询方法,变成一个 select count(*) 的查询方法。
4. 什么时候会导致不安全的分页?
PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。
只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal存储的对象。

如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。

但是如果你写出下面这样的代码,就是不安全的用法:

PageHelper.startPage(1, 10);
List<Country> list;
if(param1 != null){
    list = countryMapper.selectIf(param1);
} else {
    list = new ArrayList<Country>();
}

这种情况下由于 param1 存在null的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
上面这个代码,应该写成下面这个样子:

List<Country> list;
if(param1 != null){
    PageHelper.startPage(1, 10);
    list = countryMapper.selectIf(param1);
} else {
    list = new ArrayList<Country>();
}

这种写法就能保证安全。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
12天前
|
SQL Java 数据库连接
深入 MyBatis-Plus 插件:解锁高级数据库功能
Mybatis-Plus 提供了丰富的插件机制,这些插件可以帮助开发者更方便地扩展 Mybatis 的功能,提升开发效率、优化性能和实现一些常用的功能。
86 26
深入 MyBatis-Plus 插件:解锁高级数据库功能
|
3天前
|
SQL Java 数据库连接
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。本文讲解了最新版MP的使用教程,包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段等核心功能。
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
|
12天前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
52 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
2月前
|
SQL Java 数据库连接
解决mybatis-plus 拦截器不生效--分页插件不生效
本文介绍了在使用 Mybatis-Plus 进行分页查询时遇到的问题及解决方法。依赖包包括 `mybatis-plus-boot-starter`、`mybatis-plus-extension` 等,并给出了正确的分页配置和代码示例。当分页功能失效时,需将 Mybatis-Plus 版本改为 3.5.5 并正确配置拦截器。
581 6
解决mybatis-plus 拦截器不生效--分页插件不生效
|
2月前
|
SQL XML Java
springboot整合mybatis-plus及mybatis-plus分页插件的使用
这篇文章介绍了如何在Spring Boot项目中整合MyBatis-Plus及其分页插件,包括依赖引入、配置文件编写、SQL表创建、Mapper层、Service层、Controller层的创建,以及分页插件的使用和数据展示HTML页面的编写。
springboot整合mybatis-plus及mybatis-plus分页插件的使用
|
1月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
105 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
1月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
288 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
1月前
|
SQL Java 数据库连接
mybatis使用二:springboot 整合 mybatis,创建开发环境
这篇文章介绍了如何在SpringBoot项目中整合Mybatis和MybatisGenerator,包括添加依赖、配置数据源、修改启动主类、编写Java代码,以及使用Postman进行接口测试。
16 0
mybatis使用二:springboot 整合 mybatis,创建开发环境
|
1月前
|
Java 数据库连接 API
springBoot:后端解决跨域&Mybatis-Plus&SwaggerUI&代码生成器 (四)
本文介绍了后端解决跨域问题的方法及Mybatis-Plus的配置与使用。首先通过创建`CorsConfig`类并设置相关参数来实现跨域请求处理。接着,详细描述了如何引入Mybatis-Plus插件,包括配置`MybatisPlusConfig`类、定义Mapper接口以及Service层。此外,还展示了如何配置分页查询功能,并引入SwaggerUI进行API文档生成。最后,提供了代码生成器的配置示例,帮助快速生成项目所需的基础代码。