springboot项目中分页查询的使用解析
Spring Boot 分页查询详解
本文详细讲解 Spring Boot 中两种主流分页方案:MyBatis + PageHelper 和 MyBatis Plus 的实现方式、原理、优缺点及实际应用场景。内容涵盖配置、代码示例、注意事项和对比总结。
一、分页查询概述
分页查询是 Web 开发中处理大数据集的核心需求,其本质是 按需加载数据,避免一次性返回全部数据导致的性能问题。实现方式通常分为两类:
-
物理分页:通过 SQL 直接限制查询范围(如 LIMIT)。
-
逻辑分页:先查询全量数据,再在内存中截取分页(不推荐)。
Spring Boot 中常用 物理分页,依赖 MyBatis 的插件机制动态修改 SQL。
二、MyBatis + PageHelper 分页方案
1. 核心依赖
com.github.pagehelper pagehelper-spring-boot-starter 1.4.6
2. 配置参数(application.yml)
pagehelper: helper-dialect: mysql # 指定数据库方言(mysql/oracle/postgresql) reasonable: true # 合理化分页参数(超出范围时自动修正) support-methods-arguments: true # 支持接口参数传递分页
3. 分页实现代码
Service 层
public PageInfo getUsersByPage(int pageNum, int pageSize) { try { // 开启分页:对紧接的第一个查询生效 PageHelper.startPage(pageNum, pageSize); List users = userMapper.selectAll(); return new PageInfo(users); // 包含总条数、总页数等信息 } finally { PageHelper.clearPage(); // 清理 ThreadLocal } }
Mapper 接口
@Select("SELECT * FROM user WHERE status = 1") List selectAll();
4. 分页原理
-
ThreadLocal 传递参数:PageHelper.startPage() 将分页参数存入当前线程的 ThreadLocal。
-
拦截器重写 SQL:MyBatis 拦截器自动拼接 LIMIT offset, pageSize。
-
自动执行 COUNT 查询:生成分页数据后,自动查询总记录数。
5. 注意事项
-
调用顺序:startPage() 必须紧贴查询方法,否则分页不生效。
-
线程安全:异步或多线程场景需手动传递分页参数。
-
性能优化:复杂 SQL 可自定义 COUNT 查询:
@Select("SELECT COUNT(*) FROM user WHERE status = 1") Long countUsers(); // 指定自定义 COUNT 方法 PageHelper.startPage(1, 10).count(true).setCountSql("countUsers");
三、MyBatis Plus 分页方案
1. 核心依赖
com.baomidou mybatis-plus-boot-starter 3.5.3.1
2. 分页插件配置
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加分页拦截器,指定数据库类型 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
3. 分页实现代码
Service 层
public IPage getUsersByPage(int pageNum, int pageSize) { // 创建分页对象 Page page = new Page(pageNum, pageSize); // 执行分页查询(自动处理 SQL) return userMapper.selectPage(page, new QueryWrapper().eq("status", 1)); }
Mapper 接口
public interface UserMapper extends BaseMapper { // 继承 BaseMapper 默认提供分页方法 }
4. 分页原理
-
内置分页拦截器:自动识别 IPage 参数,重写 SQL。
-
统一分页模型:通过 IPage 接口封装分页参数和结果。
-
多数据库支持:根据 DbType 生成不同分页 SQL(如 Oracle 的 ROWNUM)。
5. 注意事项
-
Wrapper 条件:分页需结合 QueryWrapper 或自定义 SQL。
(图片来源网络,侵删) -
性能优化:大数据量分页需手动优化 COUNT 查询:
// 关闭自动 COUNT 查询 Page page = new Page(pageNum, pageSize, false); List users = userMapper.selectPage(page, wrapper); // 手动执行 COUNT 查询 page.setTotal(userMapper.selectCount(wrapper));
四、对比总结
对比维度 MyBatis + PageHelper MyBatis Plus 依赖复杂度 仅需 PageHelper 依赖 需引入 MyBatis Plus 全家桶 配置难度 需配置方言、合理化参数 仅需定义分页拦截器 侵入性 低(无需修改 Mapper) 高(需继承 BaseMapper) SQL 灵活性 支持任意复杂 SQL,手动优化空间大 简单查询高效,复杂 SQL 需自定义 线程安全 依赖 ThreadLocal,需注意异步场景 无线程安全问题(参数传递) 适用场景 已有 MyBatis 项目,需灵活分页 新项目或深度集成 MyBatis Plus 五、最佳实践与常见问题
1. 最佳实践
-
简单分页:优先使用 MyBatis Plus,减少代码量。
(图片来源网络,侵删) -
复杂 SQL:选择 PageHelper,灵活控制 SQL。
-
性能优化:
(图片来源网络,侵删)-
添加索引(如 CREATE INDEX idx_status ON user(status))。
-
避免 SELECT *,仅查询必要字段。
-
大数据量分页使用 游标分页 或 延迟关联。
2. 常见问题
Q1:分页不生效
-
原因:startPage() 调用顺序错误或未配置拦截器。
-
解决:确保 startPage() 在查询方法前调用,检查依赖和配置。
Q2:总条数(total)为 0
-
原因:COUNT 查询未匹配条件或 SQL 错误。
-
解决:手动指定 COUNT 方法或检查查询条件。
Q3:性能低下
-
优化:
-- 原始 SQL(性能差) SELECT * FROM orders ORDER BY id LIMIT 1000000, 10; -- 优化 SQL(延迟关联) SELECT * FROM orders WHERE id >= (SELECT id FROM orders ORDER BY id LIMIT 1000000, 1) ORDER BY id LIMIT 10;
六、附录:完整代码示例
PageHelper 完整示例
// Service public PageInfo getUsers(int pageNum, int pageSize) { try { PageHelper.startPage(pageNum, pageSize); List users = userMapper.selectByCondition("active"); return new PageInfo(users); } finally { PageHelper.clearPage(); } } // Mapper @Select("SELECT * FROM user WHERE status = #{status}") List selectByCondition(String status);
MyBatis Plus 完整示例
// Service public IPage getUsers(int pageNum, int pageSize) { Page page = new Page(pageNum, pageSize); QueryWrapper wrapper = new QueryWrapper(); wrapper.eq("status", "active"); return userMapper.selectPage(page, wrapper); }
-
-
-
-
-
-
-
-