news 2026/4/28 9:11:25

别再只会用selectById了!MyBatisPlus这5个查询方法,帮你搞定90%的业务场景

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再只会用selectById了!MyBatisPlus这5个查询方法,帮你搞定90%的业务场景

解锁MyBatisPlus高阶查询:5个核心方法解决90%业务难题

在Java后端开发中,数据查询是最基础却最频繁的操作。许多开发者在使用MyBatisPlus时,往往止步于简单的selectById,却不知框架早已为各种复杂场景准备了优雅的解决方案。本文将带您突破基础CRUD的局限,掌握五个核心查询方法的实战技巧,让您的代码效率提升至少50%。

1. 从单条查询到批量处理:选择正确的查询姿势

1.1 selectById:简单背后的陷阱

selectById是最基础的查询方法,但90%的开发者都没用对。考虑以下典型场景:

// 典型错误用法:循环查询 List<Long> ids = Arrays.asList(1L, 2L, 3L); List<User> users = new ArrayList<>(); for(Long id : ids) { users.add(userMapper.selectById(id)); }

这种写法会产生N+1查询问题,当ids列表较大时性能急剧下降。正确的做法是使用selectBatchIds

// 优化后的批量查询 List<User> users = userMapper.selectBatchIds(ids);

性能对比

查询方式100条记录耗时(ms)数据库连接次数
循环selectById320100
selectBatchIds451

1.2 selectOne的精准定位艺术

selectOne特别适合需要确保唯一性约束的查询场景,比如用户名、手机号等唯一字段的查询:

QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("username", "admin") .eq("status", 1); // 活跃用户 User admin = userMapper.selectOne(wrapper);

但要注意两个常见陷阱:

  1. 结果不唯一时不会报错,而是静默返回第一条
  2. 无结果时返回null而非空对象

提示:对于关键业务查询,建议先使用selectCount确认唯一性,再调用selectOne

2. 动态条件查询的两种范式

2.1 selectByMap的快速过滤

当查询条件来自前端动态参数时,selectByMap提供了最快捷的实现方式:

Map<String, Object> params = new HashMap<>(); params.put("department", "研发部"); params.put("level", "P7"); List<User> devUsers = userMapper.selectByMap(params);

适用场景

  • 简单的等值查询
  • 条件字段固定且有限
  • 快速原型开发阶段

2.2 QueryWrapper的灵活之道

对于复杂查询条件,QueryWrapper才是终极武器。它支持包括模糊查询、范围查询、嵌套查询等所有SQL能表达的条件:

QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.like("name", "张") .between("age", 25, 35) .inSql("dept_id", "SELECT id FROM department WHERE name LIKE '%技术%'") .orderByDesc("create_time"); List<User> users = userMapper.selectList(wrapper);

条件构造器常用方法

方法等价SQL适用场景
eq()=精确匹配
ne()!=不等条件
gt()>大于条件
like()LIKE '%值%'模糊查询
between()BETWEEN范围查询
in()IN多值匹配

3. 分页查询的性能优化秘籍

3.1 基础分页实现

selectPage是处理大数据量查询的必备工具,但很多开发者只停留在基础用法:

Page<User> page = new Page<>(1, 10); // 第一页,每页10条 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("status", 1); IPage<User> result = userMapper.selectPage(page, wrapper);

3.2 高级分页技巧

技巧1:禁用COUNT查询当确定不需要总记录数时:

Page<User> page = new Page<>(1, 10, false); // 第三个参数禁用count

技巧2:自定义COUNT SQL对于复杂查询,可以优化COUNT语句:

@Select("SELECT * FROM user ${ew.customSqlSegment}") @Select("SELECT COUNT(1) FROM user ${ew.customSqlSegment}") IPage<User> selectUserPage(Page<User> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);

分页参数调优建议

场景每页条数推荐配置
后台管理系统10-50默认count查询
移动端列表20-100禁用count
数据导出500-1000自定义count

4. 实战场景解决方案

4.1 多条件动态搜索

电商平台商品筛选的典型实现:

public Page<Product> searchProducts(ProductQuery query) { QueryWrapper<Product> wrapper = new QueryWrapper<>(); if (StringUtils.isNotBlank(query.getKeyword())) { wrapper.and(w -> w.like("name", query.getKeyword()) .or() .like("description", query.getKeyword())); } if (query.getCategoryId() != null) { wrapper.eq("category_id", query.getCategoryId()); } if (query.getMinPrice() != null && query.getMaxPrice() != null) { wrapper.between("price", query.getMinPrice(), query.getMaxPrice()); } if (CollectionUtils.isNotEmpty(query.getBrandIds())) { wrapper.in("brand_id", query.getBrandIds()); } return productMapper.selectPage(new Page<>(query.getPage(), query.getSize()), wrapper); }

4.2 树形结构查询

部门树形结构的递归查询方案:

public List<Department> getDepartmentTree(Long parentId) { QueryWrapper<Department> wrapper = new QueryWrapper<>(); wrapper.eq(parentId != null, "parent_id", parentId) .orderByAsc("sort_order"); List<Department> departments = departmentMapper.selectList(wrapper); departments.forEach(dept -> { List<Department> children = getDepartmentTree(dept.getId()); dept.setChildren(children); }); return departments; }

5. 性能陷阱与最佳实践

5.1 N+1查询问题

错误示例:

List<Order> orders = orderMapper.selectList(null); orders.forEach(order -> { User user = userMapper.selectById(order.getUserId()); order.setUser(user); });

优化方案:

// 先批量查询所有用户ID List<Order> orders = orderMapper.selectList(null); Set<Long> userIds = orders.stream() .map(Order::getUserId) .collect(Collectors.toSet()); // 一次性查询所有用户 Map<Long, User> userMap = userMapper.selectBatchIds(userIds) .stream() .collect(Collectors.toMap(User::getId, Function.identity())); // 内存关联 orders.forEach(order -> order.setUser(userMap.get(order.getUserId())));

5.2 索引失效场景

以下写法会导致索引失效:

QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.apply("DATE_FORMAT(create_time,'%Y-%m') = '2023-01'");

应改为:

wrapper.between("create_time", "2023-01-01", "2023-01-31");

常见索引失效情况

  1. 对字段使用函数操作
  2. 隐式类型转换
  3. 前导模糊查询(LIKE '%xxx')
  4. 使用!=<>操作符
  5. 组合索引不满足最左前缀原则
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 9:09:41

终极指南:如何用SketchUp STL插件实现3D打印的无缝转换

终极指南&#xff1a;如何用SketchUp STL插件实现3D打印的无缝转换 【免费下载链接】sketchup-stl A SketchUp Ruby Extension that adds STL (STereoLithography) file format import and export. 项目地址: https://gitcode.com/gh_mirrors/sk/sketchup-stl 你是否曾遇…

作者头像 李华
网站建设 2026/4/28 9:07:43

hyperf 测试架构工程化

─“测试架构工程化”就是把测试从“开发自己跑一下”变成“每次改代码都自动验证正确性、兼容性、性能和可上线性”。──────────下面给你一套─Hyperf 可直接落地─的完整方案。─────────────────────────────────────────…

作者头像 李华
网站建设 2026/4/28 9:05:46

ResNet残差网络:原理、实现与应用解析

1. 残差网络&#xff08;ResNet&#xff09;的核心设计理念残差网络&#xff08;Residual Networks&#xff09;在2015年由微软研究院提出&#xff0c;彻底改变了深度神经网络训练的范式。其核心创新在于引入了"跳跃连接"&#xff08;skip connection&#xff09;机制…

作者头像 李华