一、查询结果处理:List <泛型> vs IPage < 泛型 >

查询结果的承载方式直接影响数据获取效率与业务适配性,List<泛型>IPage<泛型>分别对应 “全量数据” 和 “分页数据” 两种核心需求,需根据数据规模和展示场景选择。

1. List <泛型>:全量数据的简单承载

List<泛型>是 Java 集合框架的基础类型,在 MyBatis-Plus 中用于接收所有符合条件的查询结果,不包含分页相关信息,适合数据量较小的场景。

核心特点:
  • 结构轻量:直接返回实体对象列表,无额外元数据
  • 加载方式:一次性加载所有匹配数据到内存
  • 使用门槛:无需额外配置,直接对接selectList()方法
适用场景:
  • 数据量小的基础表查询(如字典表、配置表)
  • 需要完整数据集合的批量操作(如数据导出、批量计算)
  • 简单的内部查询(如获取当前用户的关联角色列表)
代码示例:
// 1. 基础查询:获取所有状态为“启用”的用户
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("status", 1); // 条件:status=1(启用)
List<User> activeUserList = userMapper.selectList(queryWrapper);

// 2. 带排序的查询:按创建时间倒序获取用户
LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();
lambdaQuery.orderByDesc(User::getCreateTime); // 按创建时间倒序
List<User> sortedUserList = userMapper.selectList(lambdaQuery);

// 3. 结果遍历使用
for (User user : activeUserList) {
    System.out.println("用户名:" + user.getUsername() + ",状态:" + user.getStatus());
}

2. IPage <泛型>:分页数据的专业解决方案

IPage<泛型>是 MyBatis-Plus 提供的分页专属接口,用于接收分页查询结果,不仅包含当前页数据,还封装了总条数、总页数等关键分页元信息,适合数据量较大的列表场景。

核心特点:
  • 元数据完整:包含总记录数(total)、总页数(pages)、当前页码(current)、每页条数(size
  • 性能友好:基于数据库物理分页(如LIMIT语句),避免全量加载导致的内存压力
  • 配置灵活:通过Page实现类可自定义页码、每页条数,支持分页参数校验
适用场景:
  • 前端列表分页展示(如用户管理列表、订单记录列表)
  • 大数据量查询(如查询近一年的交易记录)
  • 需要分页导航的场景(如 “上一页 / 下一页 / 首页 / 末页” 功能)
代码示例:
// 1. 基础分页查询:第1页,每页10条数据
IPage<User> page = new Page<>(1, 10); // 参数1:当前页码,参数2:每页条数
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username", "admin"); // 模糊查询:username包含“admin”
IPage<User> userPage = userMapper.selectPage(page, queryWrapper);

// 2. 分页元信息获取与使用
long totalCount = userPage.getTotal(); // 总记录数
long totalPage = userPage.getPages(); // 总页数
int currentPage = (int) userPage.getCurrent(); // 当前页码
int pageSize = (int) userPage.getSize(); // 每页条数
System.out.println("总记录数:" + totalCount + ",总页数:" + totalPage);

// 3. 当前页数据获取与使用
List<User> currentPageData = userPage.getRecords(); // 当前页数据列表
for (User user : currentPageData) {
    System.out.println("页码" + currentPage + ":" + user.getUsername());
}

// 4. Lambda条件+分页:更安全的字段引用
LambdaQueryWrapper<User> lambdaPageQuery = new LambdaQueryWrapper<>();
lambdaPageQuery.ge(User::getAge, 18); // 条件:age≥18
IPage<User> lambdaUserPage = userMapper.selectPage(new Page<>(2, 10), lambdaPageQuery);

3. 两者核心区别与选择建议

对比维度 List <泛型> IPage <泛型>
数据范围 全量匹配数据 仅当前页数据
分页元信息 包含 total、pages 等完整元数据
内存占用 随数据量增大而增加 固定为 “每页条数” 级别
数据库压力 可能触发全表扫描(数据量大时) 仅扫描当前页数据
对接方法 selectList() selectPage()
选择建议:
  • 数据量≤1000 条且无需分页 → 用List<泛型>
  • 数据量 > 1000 条或需分页展示 → 必须用IPage<泛型>
  • 不确定数据量增长趋势时,优先选择IPage<泛型>(避免后期重构)

二、查询条件构造:QueryWrapper vs LambdaQueryWrapper

查询条件构造是动态 SQL 的核心,QueryWrapperLambdaQueryWrapper是 MyBatis-Plus 的两大构造工具,核心区别在于 “字段引用方式”,直接影响代码安全性和可维护性。

1. QueryWrapper:基于数据库字段名的快速构造

QueryWrapper通过数据库字段名(字符串) 指定查询条件,写法简洁直接,适合快速构建简单查询。

核心特点:

  • 字段引用:直接写数据库字段名(如"username"
  • 语法简洁:无需依赖实体类方法,上手快
  • 校验机制:无编译时校验(字段名写错需运行时发现)
适用场景:
  • 临时测试查询(如快速验证数据是否存在)
  • 数据库表结构稳定、字段名简单的场景
  • 简单条件查询(如单字段等值匹配)
代码示例:
// 1. 等值查询:获取username为“张三”的用户
QueryWrapper<User> eqQuery = new QueryWrapper<>();
eqQuery.eq("username", "张三"); // WHERE username = '张三'
User zhangsan = userMapper.selectOne(eqQuery);

// 2. 多条件组合:status=1且age≥18且create_time在2024年
QueryWrapper<User> multiQuery = new QueryWrapper<>();
multiQuery.eq("status", 1) // 条件1:status=1
          .ge("age", 18)    // 条件2:age≥18
          .between("create_time", "2024-01-01", "2024-12-31") // 条件3:时间范围
          .orderByDesc("create_time"); // 按创建时间倒序
List<User> multiConditionList = userMapper.selectList(multiQuery);

// 3. 动态条件:参数不为空时才添加条件
String keyword = "admin"; // 假设从前端传入的搜索关键词
QueryWrapper<User> dynamicQuery = new QueryWrapper<>();
if (keyword != null && !keyword.isEmpty()) {
    dynamicQuery.like("username", keyword); // 关键词非空时,添加模糊查询
}
List<User> dynamicResultList = userMapper.selectList(dynamicQuery);

2. LambdaQueryWrapper:类型安全的条件构造

LambdaQueryWrapper基于实体类方法引用(如User::getUsername)指定字段,支持编译时字段校验,能有效避免字段名拼写错误,是长期维护项目的优选。

核心特点:
  • 字段引用:通过实体类 getter 方法引用(如User::getUsername
  • 类型安全:编译时校验字段合法性(字段不存在则编译报错)
  • 重构友好:实体类字段名变更时,可同步自动重构
  • 用法灵活:支持与QueryWrapper相同的所有条件方法
适用场景:
  • 长期维护的项目(避免后期字段变更导致的隐藏 bug)
  • 字段名复杂或易拼写错误的场景(如user_registration_time
  • 复杂条件查询(如多表关联、嵌套条件)
  • 团队协作开发(统一字段引用方式,减少沟通成本)
代码示例:
// 1. 基础用法:获取age≥18且status=1的用户
LambdaQueryWrapper<User> baseLambdaQuery = new LambdaQueryWrapper<>();
baseLambdaQuery.ge(User::getAge, 18) // 条件:age≥18(引用User的getAge()方法)
               .eq(User::getStatus, 1); // 条件:status=1
List<User> adultActiveUserList = userMapper.selectList(baseLambdaQuery);

// 2. 简化用法:通过Service的lambdaQuery()直接构建
// 无需手动创建LambdaQueryWrapper,直接链式调用
List<User> quickResultList = userService.lambdaQuery()
        .like(User::getUsername, "test") // 模糊查询:username包含“test”
        .orderByAsc(User::getUsername) // 按用户名升序
        .list(); // 执行查询并返回List

// 3. 动态条件简化:无需if判断,直接传条件表达式
String searchName = "张三";
Date startTime = new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-01");
List<User> simpleDynamicList = userService.lambdaQuery()
        // 第一个参数为“条件是否生效”,true则添加该条件
        .like(searchName != null, User::getUsername, searchName)
        .ge(startTime != null, User::getCreateTime, startTime)
        .list();

// 4. 嵌套条件:OR与AND组合(例:(username含“admin” OR email含“admin”) AND status=1)
LambdaQueryWrapper<User> nestedLambdaQuery = new LambdaQueryWrapper<>();
nestedLambdaQuery.nested(q -> q.like(User::getUsername, "admin")
                               .or()
                               .like(User::getEmail, "admin"))
                 .eq(User::getStatus, 1);
List<User> nestedResultList = userMapper.selectList(nestedLambdaQuery);

3. 两者核心区别与选择建议

对比维度 QueryWrapper LambdaQueryWrapper
字段引用方式 数据库字段名字符串 实体类方法引用(如 User::getUsername)
编译时校验 无(字段错写运行时报错) 有(字段错写编译不通过)
重构支持 差(字段名变更需手动修改) 好(实体类字段变更同步重构)
学习成本 低(直接写字段名) 稍高(需了解 Lambda 表达式)
字段映射适配 需手动匹配数据库字段(如 @TableField) 自动适配实体类与数据库字段映射
选择建议:
  • 优先使用LambdaQueryWrapper:尤其新项目、长期维护项目,利用类型安全减少 bug
  • 临时查询 / 简单场景:可用QueryWrapper提升编码速度(需确保字段名正确)
  • 字段名与实体类属性不一致时(如@TableField(value = "user_name")):强制用LambdaQueryWrapper,无需关心数据库实际字段名

三、总结

  1. 结果处理选择:根据数据量和分页需求决定 —— 小数据量用List<泛型>,大数据量 / 需分页用IPage<泛型>,后者需配合Page对象初始化页码和每页条数。
  2. 条件构造选择:优先用LambdaQueryWrapper保障类型安全和可维护性,简单临时查询可用QueryWrapper;两者均可通过链式调用构建复杂条件。
  3. 组合使用原则QueryWrapper/LambdaQueryWrapper可与List/IPage任意组合(如LambdaQueryWrapper+IPage实现分页 + 类型安全条件),需根据业务场景灵活搭配。

掌握这两组工具的核心区别与用法,能显著提升 MyBatis-Plus 查询代码的效率、安全性和可维护性,适配不同场景下的查询需求。


如果您觉得这篇文章对您有帮助,请点赞关注,我会持续分享更多实用的技术文章。如有任何问题,欢迎在评论区留言讨论。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐