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 语句(如 LIMITOFFSET)在数据库层面筛选数据,属于高效且精准的实现方式。
  • 逻辑分页:先查询全部数据,再在内存中截取指定范围。这种方式在数据量较大时容易引发性能问题,因此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> 构造器的参数 currentsize 分别对应页码和每页数量。
  • 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() 方法或 QueryWrapperorderByDesc() 方法设置排序:

// 方法一:直接在 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 避免全表扫描

当查询条件未命中索引时,数据库可能执行全表扫描,导致分页变慢。此时可通过以下方式优化:

  • 在常用查询字段(如 agestatus)上建立索引。
  • 使用 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 分页与聚合函数的冲突

当查询中包含 COUNTSUM 等聚合函数时,需使用 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 应用打下坚实基础。

最新发布