mybatis plus page(建议收藏)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
在现代 Java 开发中,数据库分页查询是一项高频需求。无论是用户列表展示、订单信息筛选,还是日志数据浏览,分页技术都能有效提升系统性能和用户体验。而 MyBatis Plus Page 作为 MyBatis Plus 框架中封装分页功能的核心组件,通过简洁的 API 设计和强大的扩展性,为开发者提供了开箱即用的解决方案。本文将从基础概念到实战案例,逐步解析如何高效利用 MyBatis Plus Page 实现分页操作,并结合具体场景给出优化建议,帮助读者快速掌握这一工具的核心能力。
一、分页技术的核心概念与实现原理
1.1 分页的必要性
想象一个拥有百万级数据的用户表,若每次查询都返回全量数据,不仅会消耗大量内存,还会导致网络传输延迟。分页技术通过限制返回数据的范围,例如每次只获取前 10 条记录,既能满足用户浏览需求,又能显著降低服务器压力。
1.2 分页的两种实现方式
- 物理分页:直接通过 SQL 语句(如
LIMIT
或OFFSET
)在数据库层面筛选数据,属于高效且精准的实现方式。 - 逻辑分页:先查询全部数据,再在内存中截取指定范围。这种方式在数据量较大时容易引发性能问题,因此MyBatis Plus 默认采用物理分页。
1.3 MyBatis Plus Page 的核心设计
MyBatis Plus 将分页逻辑封装在 Page<T>
对象中,开发者只需通过构造器初始化分页参数(如当前页码、每页数量),即可直接调用查询方法。其底层通过解析 SQL 并注入分页关键字(如 LIMIT
),实现对原始查询的改造。
二、快速入门:MyBatis Plus Page 的基础用法
2.1 环境准备与依赖配置
在 Maven 项目中添加 MyBatis Plus 分页插件依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
2.2 基础分页查询
案例场景:用户列表分页
假设存在 User
实体类和对应的 Mapper 接口,以下代码演示如何实现基础分页:
// 创建 Page 对象,设置当前页为 1,每页显示 10 条
Page<User> page = new Page<>(1, 10);
// 调用 Mapper 的分页查询方法
Page<User> result = userMapper.selectPage(page, null);
// 获取分页结果
List<User> users = result.getRecords();
long total = result.getTotal();
关键点解析
Page<T>
构造器的参数current
和size
分别对应页码和每页数量。selectPage()
方法的第二个参数为查询条件(可为null
表示无条件查询)。result.getTotal()
返回总记录数,结合getSize()
可计算总页数。
三、进阶用法:结合条件与排序的复杂分页
3.1 条件分页查询
案例场景:按年龄筛选用户
通过 QueryWrapper
添加条件,实现带有过滤的分页查询:
Page<User> page = new Page<>(2, 5);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("age", 18); // 年龄大于等于 18 岁
Page<User> result = userMapper.selectPage(page, wrapper);
3.2 自定义排序规则
案例场景:按注册时间降序分页
通过 Page<T>
的 addOrder()
方法或 QueryWrapper
的 orderByDesc()
方法设置排序:
// 方法一:直接在 Page 对象中设置
page.addOrder(new OrderItem("create_time", OrderItem.desc));
// 方法二:通过 QueryWrapper 设置
wrapper.orderByDesc("create_time");
3.3 分页参数的灵活传递
在 Web 开发中,通常通过请求参数传递分页信息。以下是一个 Spring MVC 控制器的示例:
@GetMapping("/users")
public Page<User> getUsers(
@RequestParam(defaultValue = "1") int current,
@RequestParam(defaultValue = "10") int size) {
Page<User> page = new Page<>(current, size);
return userMapper.selectPage(page, null);
}
四、MyBatis Plus Page 的高级技巧与优化
4.1 分页参数的默认值配置
通过 MyBatis Plus 的全局配置,可以统一设置分页参数的默认值,避免重复代码:
@Configuration
public class MyBatisPlusConfig {
@Bean
public GlobalConfig globalConfig() {
GlobalConfig config = new GlobalConfig();
config.setDbConfig(new DbConfig()
.setPageAuto(true) // 启用物理分页
);
return config;
}
}
4.2 分页性能优化
4.2.1 避免全表扫描
当查询条件未命中索引时,数据库可能执行全表扫描,导致分页变慢。此时可通过以下方式优化:
- 在常用查询字段(如
age
、status
)上建立索引。 - 使用
lastPage()
方法查询最后一页时,避免直接计算总页数再取值。
4.2.2 分页与关联查询的结合
若需对关联表数据分页,可通过 JoinLambdaWrapper
实现,例如:
Page<Order> page = new Page<>(1, 10);
JoinLambdaWrapper<Order> wrapper = new JoinLambdaWrapper<>();
wrapper.join("LEFT JOIN user ON user.id = order.user_id")
.eq(Order::getStatus, "PAID");
Page<Order> result = orderMapper.selectJoinPage(page, wrapper);
五、常见问题与解决方案
5.1 分页查询返回空数据
可能原因:
- 条件参数拼写错误(如字段名不匹配)。
- 分页参数
current
小于 1 或size
为 0。
解决方案:
- 使用
Page<T>
的isSearchCount()
方法控制是否统计总记录数(默认true
)。 - 调试时通过
page.setSearchCount(false)
快速获取分页数据,避免统计时间开销。
5.2 分页与聚合函数的冲突
当查询中包含 COUNT
、SUM
等聚合函数时,需使用 selectObjs()
方法替代 selectPage()
,例如:
Page<Map<String, Object>> page = new Page<>(1, 10);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("AVG(age) as avg_age");
page = userMapper.selectObjs(page, wrapper);
Map<String, Object> result = page.getRecords().get(0);
六、实战案例:电商订单系统的分页实现
6.1 需求背景
某电商平台需展示用户订单列表,要求支持按状态(如“待付款”)、时间范围筛选,并按创建时间倒序分页。
6.2 实现代码
// 控制器接收参数
@GetMapping("/orders")
public Page<Order> getOrders(
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int pageSize) {
Page<Order> pageParam = new Page<>(page, pageSize);
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq(StringUtils.isNotBlank(status), "status", status)
.orderByDesc("create_time");
return orderMapper.selectPage(pageParam, wrapper);
}
6.3 扩展性设计
- 动态条件构建:通过
StringUtils
判断参数是否为空,避免硬编码条件。 - 时间范围过滤:若需支持时间范围筛选,可扩展参数并添加
between
条件:wrapper.between("create_time", startTime, endTime);
结论
通过本文的讲解,读者已掌握了 MyBatis Plus Page 从基础到进阶的使用方法,并了解了如何结合业务场景实现高效分页。无论是简单的列表展示,还是复杂的条件筛选,MyBatis Plus 通过简洁的 API 和灵活的扩展性,显著降低了分页开发的复杂度。在实际项目中,开发者还需结合数据库索引优化、分页参数校验等实践,进一步提升系统性能和健壮性。掌握这一工具,将为构建高可用的 Java 应用打下坚实基础。