Mybatis10-使用pageHelper
一、什么是分页1-1、传参页码pageNum用户会发送请求携带页码 pageNum 给服务器.每页显示的记录条数pageSize例如百度默认就是每页展示 10 条记录。实际上每一次在进行分页请求发送的时候都是要发送两个数据的页码pageNum要传送给服务器。每页显示的记录条数pageSize也要传送给服务器。前端提交表单的话数据格式uri?pageNum1pageSize101-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 前自动在语句后追加LIMIT和OFFSET从而达到分页的目的。它的核心原理是拦截Executor.query()方法对 SQL 动态改写。1. 添加依赖Mavendependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper/artifactId version5.3.2/version /dependency2、在mybatis-config.xml中添加拦截器plugins plugin interceptorcom.github.pagehelper.PageInterceptor property namehelperDialect valuemysql / property namereasonable valuetrue / /plugin /plugins配置结构说明plugins是 MyBatis插件扩展点plugin是指注册一个分页拦截器PageInterceptor每个property都是对插件行为的控制1、helperDialectmysql指定数据库方言也就是PageHelper 应该为哪种数据库生成分页语句。值对应数据库mysqlMySQL、MariaDBoracleOraclemssqlSQL ServerpostgresqlPostgreSQLsqliteSQLite建议明确指定mysql避免自动识别出错。2、reasonabletrue是否启用分页参数合理化机制常用于防止越界问题。启用后true的行为情况结果pageNum ≤ 0自动使用第一页pageNum 1pageNum 最大页自动使用最后一页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)之后紧随其后的那一次查询会被它拦截自动改写 SQL 为带LIMIT/OFFSET的分页 SQL自动再跑一条COUNT(*)统计总数把返回的ListT包装成PageT它继承ArrayListT多了total/pageNum/pageSize/...等字段。因为方法签名通常写的是ListT编译期你看到是List运行期实际是Page需要访问分页信息时手动强转或用PageInfo二次封装。startPage会自动修改 SQL加上 LIMIT 和 OFFSET配合PageInfo拿到分页数据和总页数等信息。【注意】一定要在执行sql语句之前加上PageHelper.startPage(pageNum, pageSize);控制台执行的sql语句会自动加上limit分页4. PageInfo 的使用封装分页结果PageInfoCar 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 进行二次封装用于返回分页信息给前端你手动 new1、执行过程理解当你使用 PageHelper 分页查询时执行顺序是PageHelper.startPage(pageNum, pageSize); ListArticle list articleMapper.list(...);此时list实际类型是PageArticle。接着你可以用两种不同方式去处理这个分页对象方式一使用 PageInfoPageHelper 官方推荐PageInfoArticle pageInfo new PageInfo(list);PageInfo会从Page对象中提取分页信息比如total、pages、pageNum、pageSize等然后形成一个完整的结构{ total: 96, pageNum: 1, pageSize: 10, list: [ ... ], pages: 10, hasNextPage: true, ... }这种方式比较通用不用你自己定义PageBean。方式二使用自定义 PageBean更灵活、风格统一项目中很多人不想直接返回PageInfo因为PageInfo字段太多前端不一定都需要业务希望统一分页返回结构比如只返回total和items避免直接暴露三方库类型。于是就会这样写PageArticle page (PageArticle) list; PageBeanArticle pb new PageBean(); pb.setTotal(page.getTotal()); pb.setItems(page.getResult());最终返回给 Controller 或前端的就是你自己的封装。2、总结三者关系层次对象角色定位你需要做什么底层PageMyBatis 查询结果 分页信息PageHelper 自动生成中间层PageInfo对Page的通用封装官方提供、可直接返回上层PageBean你自己定义的业务封装为项目返回结构统一3、一句话总结Page是查询结果容器PageInfo是PageHelper 提供的结果封装类PageBean是你项目自定义的返回格式。四、Page对象list的实际运行时类型确实是PageArticle但编译器只知道它是ListArticle所以如果你要访问分页信息如 getTotal()必须手动强转。1、表面上看返回的是ListArticle你调用的 Mapper 方法比如ListArticle list articleMapper.list(userId, categoryId, state);在 Mapper XML 中大概是select idlist resultTypeArticle SELECT * FROM article WHERE ... /select这个方法的返回值声明是ListArticle所以编译器只知道“这玩意儿返回一个 List。”2、PageHelper 干了什么当你调用PageHelper.startPage(pageNum, pageSize);的时候PageHelper 会通过MyBatis 的拦截器机制Interceptor做两件事拦截 SQL 执行它会在执行真正的 SQL 之前修改你的 SQL 为分页版本比如SELECT * FROM article LIMIT 0, 10;同时再执行一条统计总数的 SQLSELECT COUNT(*) FROM article;拦截返回结果它把 Mapper 返回的原始ListArticle包装成一个特殊的PageArticle对象。而Page本身就是ArrayList的子类public class PageE extends ArrayListE implements Closeable { private long total; private int pageNum; private int pageSize; ... }所以Page既是List又多了分页信息total、pages、pageNum等。3、为什么还需要强转因为编译器类型检查Mapper 的方法签名是ListArticle所以编译器只认 List运行时实际类型PageHelper 返回的实际对象是PageArticle只是子类对象赋值给了父类引用。举个例子ArrayListString list new ArrayList(); ListString data list; // ✅ 子类赋给父类引用 data.size(); // OKList的方法 data.getTotal(); // ❌ 报错List没有getTotal()要想用 Page 的方法如getTotal()必须PageArticle page (PageArticle) list;4、总结一句话记忆PageHelper 用AOP 拦截把原来的List包装成PagePage实际上是List的子类编译器不知道运行时知道所以如果你要访问分页属性必须强转。5、可选方案直接用 PageInfo不需要强转PageHelper.startPage(pageNum, pageSize); ListArticle list articleMapper.list(...); PageInfoArticle pageInfo new PageInfo(list); System.out.println(pageInfo.getTotal()); System.out.println(pageInfo.getList());PageInfo会自动识别 list 是不是 Page 类型不需要你强转非常方便。