掌握分页艺术:MyBatis与MyBatis-Plus实战指南(10年Java亲授)

摘要:在高并发、大数据量的现代应用中,分页是每个Java开发者绕不开的核心技能。本文以10年+实战经验为基础,深入浅出地解析MyBatis与MyBatis-Plus的分页实现,配以流程图、代码示例和避坑指南,助你彻底告别"内存溢出"和"全表扫描"噩梦。全文2300+字,零基础也能轻松上手!


一、为什么分页是每个Java开发者的必修课?(分页的价值)

想象一下:你的电商平台有100万商品数据,用户点击"全部商品"时,如果一次性加载所有数据------服务器内存瞬间爆炸,前端页面卡成PPT,数据库CPU直接飙到100% 。这就是不分页的灾难现场

分页的核心价值在于:

  • 性能优化:避免全表扫描,减少数据库压力(如图1所示,分页查询仅扫描10行而非100万行)
  • 用户体验:用户无需等待" eternity loading",流畅翻页提升转化率
  • 资源节约:降低网络传输量,节省带宽成本

通俗理解 :分页就像图书馆的索引卡------你不需要搬出整栋楼的书,只需按"第3排第5架"精准取书。在Java世界中,offset/limitpage/size 就是你的"索引卡"。


二、分页基础:3分钟搞懂核心概念

分页本质是数据切片,主流实现有两种模式:

模式 参数示例 优点 缺点
偏移量模式 offset=0, limit=10 兼容性好,SQL标准 深分页时性能骤降(如offset=10000)
页码模式 page=1, size=10 开发友好,符合用户习惯 需转换为offset/limit

关键公式
offset = (page - 1) * size

例如:第3页每页10条 → offset = (3-1)*10 = 20

💡 经验之谈 :10年踩坑总结------永远用页码模式开发(用户说"第5页"而非"跳过40条"),但底层需转为偏移量模式执行SQL。


三、MyBatis分页实战:从RowBounds到PageHelper(附避坑指南)

MyBatis原生支持有限,但通过插件可优雅实现分页。下面分两步拆解:

3.1 原生方案:RowBounds(慎用!)

MyBatis内置RowBounds对象,但仅适用于内存分页 (先查全量再截取),大数据量下是性能毒药

java 复制代码
// 错误示范:全量查询后内存分页!
List<User> users = sqlSession.selectList("UserMapper.selectUsers", null, 
    new RowBounds(0, 10)); // offset=0, limit=10

致命问题

  • SQL实际执行:SELECT * FROM user(无LIMIT)
  • 数据库返回100万条 → Java内存OOM
  • 仅适用于<1000条的小数据集

3.2 生产级方案:PageHelper插件(MyBatis分页之王)

PageHelper是GitHub 15k+ Star的分页神器,自动重写SQL添加LIMIT

Step 1:添加依赖(Maven)

xml 复制代码
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.6</version>
</dependency>

Step 2:配置文件(application.yml)

yaml 复制代码
pagehelper:
  helper-dialect: mysql  # 指定数据库类型
  reasonable: true       # 启用合理化(页码>总页数时自动查最后一页)
  support-methods-arguments: true # 允许传参控制分页

Step 3:代码实现(Controller层)

java 复制代码
@GetMapping("/users")
public PageInfo<User> getUsers(@RequestParam(defaultValue = "1") int pageNum, 
                              @RequestParam(defaultValue = "10") int pageSize) {
    // 关键:开启分页(拦截后续第一个查询)
    PageHelper.startPage(pageNum, pageSize);
    
    // 执行Mapper查询(无需修改SQL!)
    List<User> list = userMapper.selectAll(); 
    
    // 封装分页对象(含总记录数、总页数等)
    return new PageInfo<>(list);
}

Step 4:Mapper XML(无需LIMIT!)

xml 复制代码
<select id="selectAll" resultType="User">
    SELECT * FROM user
    <!-- PageHelper自动在运行时重写为:SELECT * FROM user LIMIT 0,10 -->
</select>

避坑指南(10年血泪经验)

  1. 必须紧跟第一个查询startPage()后必须立即调用select,否则失效
  2. 禁止嵌套分页:一个方法内多次分页会导致错乱
  3. 深分页优化offset>10000时改用WHERE id > last_max_id(游标分页)

四、MyBatis-Plus分页:一行代码解放生产力(效率提升300%)

作为MyBatis的超级增强版,MyBatis-Plus内置分页插件,无需XML,纯Java API,彻底告别SQL改写烦恼。

4.1 为什么选择MyBatis-Plus?

  • 零配置起步:Spring Boot集成仅需1个注解
  • 强类型APIPage<T>对象直接操作,告别字符串参数
  • 自动优化 :深分页时智能切换COUNT查询策略

4.2 三步实现分页(比泡面还简单)

Step 1:添加依赖(Maven)

xml 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

Step 2:配置分页插件(Config类)

java 复制代码
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件(核心!)
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

Step 3:Mapper继承BaseMapper(无需写SQL!)

java 复制代码
public interface UserMapper extends BaseMapper<User> {
    // MyBatis-Plus已内置selectPage方法
}

Step 4:Service层调用(真·一行代码)

java 复制代码
public IPage<User> getUserPage(int pageNum, int pageSize) {
    // 创建分页对象:当前页=1,每页10条
    Page<User> page = new Page<>(pageNum, pageSize);
    
    // 调用内置分页方法(自动处理COUNT查询和LIMIT)
    return userMapper.selectPage(page, null); 
}

返回结果解析

json 复制代码
{
  "records": [/* 当前页10条数据 */],
  "total": 1000000,   // 自动统计的总记录数
  "size": 10,         // 每页大小
  "current": 1,       // 当前页码
  "pages": 100000     // 总页数
}

4.3 高阶技巧:条件分页 & 性能调优

场景:查询"年龄>25的用户,按注册时间倒序分页"

java 复制代码
// 构建查询条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 25).orderByDesc("create_time");

// 分页查询(自动应用条件)
IPage<User> page = userMapper.selectPage(
    new Page<>(1, 10), 
    wrapper
);

性能调优关键点

  1. 关闭COUNT查询 :当总页数不重要时(如无限滚动列表)

    java 复制代码
    page.optimizeCountSql(false); // 省去COUNT查询,速度提升50%
  2. 自定义COUNT查询 :复杂SQL时避免SELECT COUNT(*)慢查询

    java 复制代码
    wrapper.select("id"); // 只查主键提升COUNT效率
  3. 深分页优化offset>10000时启用maxMethodLimit

    java 复制代码
    page.setMaxLimit(10000L); // 超过1万页自动抛异常

五、MyBatis vs MyBatis-Plus分页:终极对比表

维度 MyBatis + PageHelper MyBatis-Plus 推荐场景
配置复杂度 需额外集成插件 内置分页,仅需1个拦截器 新项目首选MP
代码侵入性 XML需保持无LIMIT 无XML,纯Java调用 追求简洁的团队
深分页性能 依赖PageHelper优化 内置maxMethodLimit保护 大数据量系统
学习成本 需理解拦截器机制 Page对象直觉式操作 新人快速上手
灵活性 支持自定义方言 依赖内置DbType 多数据库混合项目

10年架构师建议

  • 老项目迁移:用PageHelper,兼容性强
  • 新项目启动无脑选MyBatis-Plus,减少70%分页代码量
  • 亿级数据场景 :结合游标分页(WHERE id > last_id)+ 缓存总记录数

六、分页的终极陷阱:90%开发者踩过的3个大坑

坑1:深分页导致数据库崩溃

现象SELECT * FROM order LIMIT 100000, 10 执行超5秒
原理 :MySQL需扫描10万行再丢弃
解法

  • 游标分页:WHERE id > 100000 ORDER BY id LIMIT 10
  • 提前缓存:Redis存储第10000页的起始ID

坑2:COUNT查询拖垮性能

现象 :总记录数100万时,COUNT(*)耗时3秒
解法

  • 估算总数:SELECT (SELECT COUNT(*) FROM user) AS total, * FROM user LIMIT 0,10
  • 业务妥协:显示"超过10万条"而非精确值(如淘宝搜索)

坑3:分页与排序冲突

现象 :排序字段非唯一(如按create_time),同一页数据翻页错乱
解法

  • 排序字段追加唯一ID:ORDER BY create_time DESC, id DESC

结语:分页不是技巧,而是工程思维

分页看似简单,却浓缩了数据库优化、内存管理、用户体验的全栈思维 。作为10年Java老兵,我见过太多团队因分页设计失误导致线上事故------从OOM重启到用户投诉,根源往往是一行缺失的LIMIT

行动指南

  1. 永远用MyBatis-Plus:新项目拒绝重复造轮子
  2. 深分页必做防护 :设置maxLimit或切换游标模式
  3. 性能压测先行:模拟10万数据验证分页SQL

最后送大家一句我的座右铭:"能分页的场景不分页,和自行车道开坦克没区别"。掌握本文技术,你不仅能写出健壮的分页代码,更能培养出对系统边界的敬畏之心------这才是高级开发者的真正分水岭。

附:扩展学习资源

本文由10年Java架构师原创,转载请注明出处。实践出真知,现在就去你的项目里加个分页试试吧!(全文2380字)

相关推荐
该用户已不存在6 分钟前
OpenJDK、Temurin、GraalVM...到底该装哪个?
java·后端
TT哇37 分钟前
@[TOC](计算机是如何⼯作的) JavaEE==网站开发
java·redis·java-ee
Tina学编程43 分钟前
48Days-Day19 | ISBN号,kotori和迷宫,矩阵最长递增路径
java·算法
青川入梦1 小时前
MyBatis极速通关上篇:Spring Boot环境搭建+用户管理实战
java·开发语言·mybatis
执子手 吹散苍茫茫烟波1 小时前
leetcode415. 字符串相加
java·leetcode·字符串
小韩博1 小时前
网络安全(Java语言)脚本 汇总(二)
java·安全·web安全
萤丰信息1 小时前
技术赋能安全:智慧工地构建城市建设新防线
java·大数据·开发语言·人工智能·智慧城市·智慧工地
带刺的坐椅2 小时前
Java MCP 的鉴权?好简单的啦
java·鉴权·mcp·solon-ai
Pocker_Spades_A2 小时前
飞算JavaAI家庭记账系统:从收支记录到财务分析的全流程管理方案
java·开发语言