Mybatis10-使用pageHelper

一、什么是分页

1-1、传参

页码:pageNum(用户会发送请求,携带页码 pageNum 给服务器.)

每页显示的记录条数:pageSize,例如百度默认就是每页展示 10 条记录。

实际上每一次在进行分页请求发送的时候,都是要发送两个数据的:

  1. 页码 pageNum 要传送给服务器。
  2. 每页显示的记录条数 pageSize 也要传送给服务器。

前端提交表单的话,数据格式:

uri?pageNum=1&pageSize=10

1-2、关于mysql当中的分页sql应该怎么写?

limit关键字。

limit 语法格式:

limit 开始下标,显示的记录条数,

示例:limit startIndex, pageSize(从第startIndex条开始,显示pageSize条)

select * from t_car limit 0, 3;

select * from t_car limit 2; 和 select * from t_car limit 0, 2;是等效的。

mysql当中起始行的下标从0开始,即:第一条记录的下标是0

二、使用 PageHelper 插件(推荐)

PageHelper 是 MyBatis 最常用的分页插件,它通过拦截器方式,在执行 SQL 前自动在语句后追加 LIMITOFFSET,从而达到分页的目的。

它的核心原理是:拦截 Executor.query() 方法,对 SQL 动态改写。

1. 添加依赖(Maven)

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

 2、在 mybatis-config.xml中添加拦截器

<plugins>
  <plugin interceptor="com.github.pagehelper.PageInterceptor">
    <property name="helperDialect" value="mysql" />
    <property name="reasonable" value="true" />
  </plugin>
</plugins>

配置结构说明

  • <plugins> 是 MyBatis 插件扩展点

  • <plugin> 是指注册一个分页拦截器(PageInterceptor

  • 每个 <property> 都是对插件行为的控制


(1)、helperDialect="mysql"

指定数据库方言,也就是 PageHelper 应该为哪种数据库生成分页语句

对应数据库
mysqlMySQL、MariaDB
oracleOracle
mssqlSQL Server
postgresqlPostgreSQL
sqliteSQLite

建议:明确指定 mysql,避免自动识别出错。


(2)、reasonable="true"

是否启用 分页参数合理化机制,常用于防止越界问题

启用后(true)的行为:

情况结果
pageNum ≤ 0自动使用第一页(pageNum = 1)
pageNum > 最大页自动使用最后一页
pageSize ≤ 0查所有记录

示例:

PageHelper.startPage(-1, 10); // 实际使用 pageNum = 1
PageHelper.startPage(9999, 10); // 如果最大页是 50,会用 pageNum = 50

推荐开启,防止前端传参错导致崩溃或空页。

在使用 PageHelper 时,它的页码参数(Page Number)是从 1 开始

因为,PageHelper 的拦截器会在底层自动帮你算出数据库真正需要的偏移量(Offset),计算公式是 (pageNum - 1) * pageSize

3. 基本用法

你只需要在查询之前调用

PageHelper是 MyBatis 拦截器。当你调用 PageHelper.startPage(pageNum, pageSize) 之后,紧随其后的那一次查询 会被它拦截:

  1. 自动改写 SQL 为带 LIMIT/OFFSET 的分页 SQL;

  2. 自动再跑一条 COUNT(*) 统计总数

  3. 把返回的 List<T> 包装成 Page<T>(它继承 ArrayList<T>,多了 total/pageNum/pageSize/... 等字段)。

因为方法签名通常写的是 List<T>,编译期你看到是 List运行期实际是 Page

需要访问分页信息时,手动强转或用 PageInfo 二次封装

startPage 会自动修改 SQL,加上 LIMIT 和 OFFSET,配合 PageInfo 拿到分页数据和总页数等信息。

【注意】:

一定要在执行sql语句之前加上:PageHelper.startPage(pageNum, pageSize);

控制台执行的sql语句会自动加上limit分页

4. PageInfo 的使用(封装分页结果)

PageInfo<Car> pageInfo = new PageInfo<>(cars);

直接将查询结果给到pageInfo即可 

方法返回值含义
getTotal()long总记录数
getPages()int总页数
getPageNum()int当前页码
getPageSize()int每页大小
getList()List当前页的数据列表
isHasNextPage()boolean是否有下一页
isIsLastPage()boolean是否是最后一页

5、XML 文件中的配合使用

你的 XML 中写的仍是普通 SQL:

分页逻辑由 Java 代码调用 PageHelper.startPage() 控制,XML 无需修改

三、PageHelper 中 Page、PageInfo

名称来源用途谁创建的
PagePageHelper 内部类MyBatis 查询结果分页包装(实现了 List 接口)PageHelper 自动生成
PageInfoPageHelper 提供的封装类对 Page 进行二次封装,用于返回分页信息给前端你手动 new

1、执行过程理解

当你使用 PageHelper 分页查询时,执行顺序是:

PageHelper.startPage(pageNum, pageSize);
List<Article> list = articleMapper.list(...);

此时 list 实际类型是 Page<Article>

接着你可以用两种不同方式去处理这个分页对象:


方式一:使用 PageInfo(PageHelper 官方推荐)

PageInfo<Article> pageInfo = new PageInfo<>(list);

PageInfo 会从 Page 对象中提取分页信息(比如 totalpagespageNumpageSize 等),
然后形成一个完整的结构:

{
  "total": 96,
  "pageNum": 1,
  "pageSize": 10,
  "list": [ ... ],
  "pages": 10,
  "hasNextPage": true,
  ...
}

这种方式比较通用,不用你自己定义 PageBean


方式二:使用自定义 PageBean(更灵活、风格统一)

项目中很多人不想直接返回 PageInfo,因为:

  • PageInfo 字段太多,前端不一定都需要;

  • 业务希望统一分页返回结构(比如只返回 totalitems);

  • 避免直接暴露三方库类型。

于是就会这样写:

Page<Article> page = (Page<Article>) list;

PageBean<Article> pb = new PageBean<>();
pb.setTotal(page.getTotal());
pb.setItems(page.getResult());

最终返回给 Controller 或前端的就是你自己的封装。


2、总结三者关系

层次对象角色定位你需要做什么
底层PageMyBatis 查询结果 + 分页信息PageHelper 自动生成
中间层PageInfoPage 的通用封装官方提供、可直接返回
上层PageBean你自己定义的业务封装为项目返回结构统一

3、一句话总结

  • Page查询结果容器
  • PageInfoPageHelper 提供的结果封装类
  • PageBean你项目自定义的返回格式

四、Page对象

list 的实际运行时类型确实是 Page<Article>,但编译器只知道它是 List<Article>,所以如果你要访问分页信息(如 getTotal()),必须手动强转。


1、表面上看:返回的是 List<Article>

你调用的 Mapper 方法,比如:

List<Article> list = articleMapper.list(userId, categoryId, state);

在 Mapper XML 中,大概是:

<select id="list" resultType="Article">
    SELECT * FROM article WHERE ...
</select>

这个方法的返回值声明是 List<Article>,所以编译器只知道:

“这玩意儿返回一个 List。”


2、PageHelper 干了什么?

当你调用:

PageHelper.startPage(pageNum, pageSize);

的时候,PageHelper 会通过 MyBatis 的拦截器机制Interceptor做两件事:

  1. 拦截 SQL 执行
    它会在执行真正的 SQL 之前,修改你的 SQL 为分页版本,比如:

    SELECT * FROM article LIMIT 0, 10;
    

    同时再执行一条统计总数的 SQL

    SELECT COUNT(*) FROM article;
    
  2. 拦截返回结果
    把 Mapper 返回的原始 List<Article> 包装成一个特殊的 Page<Article> 对象
    Page 本身就是 ArrayList 的子类

    public class Page<E> extends ArrayList<E> implements Closeable {
        private long total;
        private int pageNum;
        private int pageSize;
        ...
    }
    

    所以 Page 既是 List,又多了分页信息(total、pages、pageNum等)。


3、为什么还需要强转?

因为:

  • 编译器类型检查
    Mapper 的方法签名是 List<Article>,所以编译器只认 List;

  • 运行时实际类型
    PageHelper 返回的实际对象是 Page<Article>,只是子类对象赋值给了父类引用

举个例子:

ArrayList<String> list = new ArrayList<>();
List<String> data = list;  // ✅ 子类赋给父类引用

data.size();     // OK,List的方法
data.getTotal(); // ❌ 报错,List没有getTotal()

要想用 Page 的方法(如 getTotal()),必须:

Page<Article> page = (Page<Article>) list;

4、总结一句话记忆

PageHelper 用 AOP 拦截 把原来的 List 包装成 Page
Page 实际上是 List 的子类,

编译器不知道,运行时知道
所以如果你要访问分页属性,必须强转


5、可选方案:直接用 PageInfo(不需要强转)

PageHelper.startPage(pageNum, pageSize);
List<Article> list = articleMapper.list(...);

PageInfo<Article> pageInfo = new PageInfo<>(list);

System.out.println(pageInfo.getTotal());
System.out.println(pageInfo.getList());

PageInfo 会自动识别 list 是不是 Page 类型,不需要你强转,非常方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值