MyBatis-Plus进行分页查询优化的实践指南

 更新时间:2025年07月10日 08:12:24   作者:码农阿豪@新空间  
在实际开发中,分页查询是常见的需求,尤其是需要关联其他表获取额外信息的场景,本文将介绍如何使用 MyBatis-Plus 进行高效分页查询,并结合关联查询优化数据填充,需要的可以了解下

1. 引言

在实际开发中,分页查询是常见的需求,尤其是需要关联其他表获取额外信息的场景。例如,在任务管理系统中,查询任务列表时,除了任务基本信息外,还需要显示任务创建者的用户名(通常存储在用户表中)。直接使用循环单条查询会导致性能问题,而合理的优化可以显著提升查询效率。

本文将介绍如何使用 MyBatis-Plus 进行高效分页查询,并结合关联查询优化数据填充,最终封装成 VO(View Object) 返回给前端。我们将分析两种优化方案:

  • 单条查询填充(适用于数据量较小的情况)
  • 批量查询填充(适用于大数据量,提高性能)

2. 基础分页查询实现

首先,我们回顾基础的 MyBatis-Plus 分页查询实现:

Page<TaskManagement> pageRequest = new Page<>(request.getPage(), request.getPageSize());

// 构建查询条件
LambdaQueryWrapper<TaskManagement> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(request.getStatus())) {
    wrapper.like(TaskManagement::getStatus, request.getStatus());
}

// 执行分页查询
IPage<TaskManagement> taskManagementIPage = taskManagementService.page(pageRequest, wrapper);

这段代码完成了:

  • 分页参数构建(Page 对象)
  • 动态条件查询(LambdaQueryWrapper
  • 执行分页查询(taskManagementService.page()

但返回的数据仅包含 TaskManagement 表的信息,缺少关联的用户名(creatorUsername),我们需要进一步优化。

3. 优化方案1:单条查询填充(适用于小数据量)

3.1 定义VO对象

首先,定义一个 TaskManagementVO,增加 creatorUsername 字段:

@Data
public class TaskManagementVO {
    private Long id;
    private String taskName;
    private String status;
    // 其他任务字段...
    
    private Long userId;       // 创建者ID
    private String creatorUsername; // 创建者姓名(需关联用户表查询)
}

3.2 转换分页数据并填充用户名

使用 IPage.convert() 方法进行数据转换,并在转换过程中查询用户名:

IPage<TaskManagementVO> voPage = taskManagementIPage.convert(task -> {
    TaskManagementVO vo = new TaskManagementVO();
    BeanUtils.copyProperties(task, vo); // 使用Spring BeanUtils复制属性
    
    if (task.getUserId() != null) {
        // 查询用户信息
        User user = userService.getById(task.getUserId());
        if (user != null) {
            vo.setCreatorUsername(user.getName());
        }
    }
    return vo;
});

return voPage;

3.3 优缺点分析

优点:

  • 代码简单,易于理解
  • 适用于数据量较小的场景

缺点:

  • N+1查询问题:如果每页有N条数据,会额外执行N次用户查询,性能较差
  • 不适合大数据量分页

4. 优化方案2:批量查询填充(适用于大数据量)

4.1 优化思路

  • 先收集所有 userId
  • 批量查询用户数据,减少数据库交互
  • 使用 Map 存储用户信息,提高查询效率

4.2 代码实现

// 1. 提取所有不重复的userId
List<Long> userIds = taskManagementIPage.getRecords().stream()
        .map(TaskManagement::getUserId)
        .filter(Objects::nonNull)
        .distinct()
        .collect(Collectors.toList());

// 2. 批量查询用户信息,并转为Map<userId, User>
Map<Long, User> userMap = userIds.isEmpty() 
        ? Collections.emptyMap()
        : userService.listByIds(userIds).stream()
                .collect(Collectors.toMap(User::getId, Function.identity()));

// 3. 转换分页数据并填充用户名
IPage<TaskManagementVO> voPage = taskManagementIPage.convert(task -> {
    TaskManagementVO vo = new TaskManagementVO();
    BeanUtils.copyProperties(task, vo);
    
    if (task.getUserId() != null && userMap.containsKey(task.getUserId())) {
        vo.setCreatorUsername(userMap.get(task.getUserId()).getName());
    }
    return vo;
});

return voPage;

4.3 优化点分析

优点:

  • 减少数据库查询次数:无论分页数据有多少条,用户表只查询1次
  • 性能更高:适合大数据量场景

缺点:

  • 代码稍复杂,需要额外处理 Map 结构
  • 如果 userIds 过多(如 IN 查询超过1000条),可能需要分批查询

5. 进一步优化:使用JOIN查询(SQL层面优化)

如果 TaskManagementUser 表关联频繁,可以在 SQL 层面直接使用 JOIN 查询,避免多次查询:

5.1 自定义SQL查询

<!-- TaskManagementMapper.xml -->
<select id="selectTaskPageWithUser" resultType="TaskManagementVO">
    SELECT 
        t.*,
        u.name AS creatorUsername
    FROM 
        task_management t
    LEFT JOIN 
        user u ON t.user_id = u.id
    <where>
        <if test="status != null and status != ''">
            t.status LIKE CONCAT('%', #{status}, '%')
        </if>
    </where>
</select>

5.2 在Service层调用

// 使用自定义分页查询
IPage<TaskManagementVO> voPage = taskManagementMapper.selectTaskPageWithUser(
        pageRequest, 
        request.getStatus());

5.3 优缺点

优点:

  • 最高效:只需1次SQL查询
  • 适合复杂关联查询

缺点:

  • 需要手写SQL,不够灵活
  • 如果关联表过多,SQL可能较复杂

6. 总结与最佳实践

方案适用场景性能代码复杂度
单条查询填充数据量小(<100条/页)较差(N+1查询)
批量查询填充数据量较大(100-1000条/页)较好(2次查询)
JOIN查询大数据量、频繁关联查询最优(1次查询)高(需手写SQL)

6.1 推荐选择

  • 小型项目/简单查询 → 单条查询填充
  • 中大型项目/数据量较大 → 批量查询填充
  • 高性能要求/复杂关联 → JOIN查询

6.2 完整优化代码示例

public IPage<TaskManagementVO> getTaskPage(TaskPageRequest request) {
    // 1. 构建分页查询
    Page<TaskManagement> pageRequest = new Page<>(request.getPage(), request.getPageSize());
    LambdaQueryWrapper<TaskManagement> wrapper = new LambdaQueryWrapper<>();
    if (StringUtils.isNotBlank(request.getStatus())) {
        wrapper.like(TaskManagement::getStatus, request.getStatus());
    }
    
    // 2. 执行分页查询
    IPage<TaskManagement> taskManagementIPage = taskManagementService.page(pageRequest, wrapper);
    
    // 3. 批量查询用户信息
    List<Long> userIds = taskManagementIPage.getRecords().stream()
            .map(TaskManagement::getUserId)
            .filter(Objects::nonNull)
            .distinct()
            .collect(Collectors.toList());
    
    Map<Long, User> userMap = userIds.isEmpty() 
            ? Collections.emptyMap()
            : userService.listByIds(userIds).stream()
                    .collect(Collectors.toMap(User::getId, Function.identity()));
    
    // 4. 转换VO并填充用户名
    return taskManagementIPage.convert(task -> {
        TaskManagementVO vo = new TaskManagementVO();
        BeanUtils.copyProperties(task, vo);
        
        if (task.getUserId() != null && userMap.containsKey(task.getUserId())) {
            vo.setCreatorUsername(userMap.get(task.getUserId()).getName());
        }
        return vo;
    });
}

7. 结语

优化分页查询的核心在于 减少数据库交互次数,本文介绍了三种优化方案,适用于不同场景。在实际开发中,应根据业务需求和数据量选择最合适的方案。

进一步优化建议:

  • 使用 缓存(Redis) 存储用户信息,减少数据库查询
  • 对于超大数据量(如10万+),考虑 游标分页 或 Elasticsearch 优化

到此这篇关于MyBatis-Plus进行分页查询优化的实践指南的文章就介绍到这了,更多相关mybatis-plus分页查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Springboot+Bootstrap实现增删改查实战

    Springboot+Bootstrap实现增删改查实战

    这篇文章主要介绍了Springboot+Bootstrap实现增删改查实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • 基于request获取访问者真实IP代码示例

    基于request获取访问者真实IP代码示例

    这篇文章主要介绍了基于request获取访问者真实IP代码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Spring TaskScheduler使用实例解析

    Spring TaskScheduler使用实例解析

    这篇文章主要介绍了Spring TaskScheduler使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • SpringBoot的单体和分布式的任务架构详解

    SpringBoot的单体和分布式的任务架构详解

    在Spring Boot生态中,定时任务框架的选择需根据架构类型(单体或分布式)和功能需求进行权衡,以下从框架特性、适用场景及Spring Boot集成方式等角度,详细梳理主流的定时任务框架及其分类,本文给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2025-05-05
  • Spring Boot 中的横切逻辑统一治理方案

    Spring Boot 中的横切逻辑统一治理方案

    拦截器是Spring框架提供的核心功能之一,核心用于统一拦截用户的请求,文章主要介绍了Spring框架中的拦截器、统一数据返回格式和统一异常处理,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2026-02-02
  • Spring如何消除代码中的if-else/switch-case

    Spring如何消除代码中的if-else/switch-case

    这篇文章主要给大家介绍了关于Spring如何消除代码中if-else/switch-case的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04
  • Java多线程run方法中直接调用service业务类应注意的问题及解决

    Java多线程run方法中直接调用service业务类应注意的问题及解决

    这篇文章主要介绍了Java多线程run方法中直接调用service业务类应注意的问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • java实现对map的字典序排序操作示例

    java实现对map的字典序排序操作示例

    这篇文章主要介绍了java实现对map的字典序排序操作,结合实例形式分析了java参照微信官网算法实现的字典序排序操作相关实现技巧,需要的朋友可以参考下
    2019-07-07
  • Spring中事务几个常见的问题解决

    Spring中事务几个常见的问题解决

    这篇文章主要介绍了Spring中事务几个常见的问题解决,事务这个概念是数据库层面的,Spring只是基于数据库中的事务进行扩展,以及提供了一些能让程序员更新方便操作事务的方式
    2022-08-08
  • 一文盘点Spring MVC中用于处理HTTP请求的常用注解

    一文盘点Spring MVC中用于处理HTTP请求的常用注解

    这篇文章主要为大家详细介绍了Spring MVC中用于处理HTTP请求的常用注解,包括@RequestMapping,@RequestController,@RequestParam等,希望对大家有所帮助
    2026-03-03

最新评论