MyBatis-Plus LambdaQueryWrapper实战:告别手写SQL

背景

做Java后端的应该都用过MyBatis-Plus,但很多人还是习惯写XML或者用QueryWrapper拼字符串。字符串拼字段名有个问题:字段名写错了编译器不报错,重构时IDE也不会帮你改,只能等单元测试或者线上报错才能发现。

LambdaQueryWrapper解决了这个问题。它用Java的Lambda表达式引用字段,编译期就能检查字段名是否正确。用了一年多,分享一些高频用法和踩坑经验。

基础用法

假设有个用户表:

kotlin 复制代码
@Data
@TableName("sys_user")
public class User {
    private Long id;
    private String username;
    private String email;
    private Integer status;    // 0禁用 1启用
    private Long deptId;
    private LocalDateTime createTime;
}

等值查询

perl 复制代码
// 老写法:字符串拼字段
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);  // 写错不报错
​
// Lambda写法:编译期检查
LambdaQueryWrapper<User> lambda = new LambdaQueryWrapper<>();
lambda.eq(User::getUsername, username);  // 字段名错了编译直接红

多条件组合

sql 复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1)
       .like(User::getUsername, keyword)
       .ge(User::getCreateTime, startTime)
       .le(User::getCreateTime, endTime)
       .orderByDesc(User::getCreateTime);
​
List<User> users = userMapper.selectList(wrapper);

条件动态拼接

这是业务代码里最常见的场景------前端传了哪些参数就拼哪些条件:

css 复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1)
       .eq(StrUtil.isNotBlank(dto.getEmail()), User::getEmail, dto.getEmail())
       .like(StrUtil.isNotBlank(dto.getUsername()), User::getUsername, dto.getUsername())
       .eq(dto.getDeptId() != null, User::getDeptId, dto.getDeptId())
       .ge(dto.getStartTime() != null, User::getCreateTime, dto.getStartTime())
       .le(dto.getEndTime() != null, User::getCreateTime, dto.getEndTime());

eq(condition, field, value) 第一个参数是布尔条件,为 false 时自动跳过这个条件。不用写一堆 if-else,一个链式调用搞定。

IN 查询 + 分页

ini 复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.in(CollectionUtil.isNotEmpty(deptIds), User::getDeptId, deptIds)
       .eq(User::getStatus, 1);
​
Page<User> page = new Page<>(pageNum, pageSize);
userMapper.selectPage(page, wrapper);

只查部分字段

有时候不需要查全部字段,用 select 指定:

ini 复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId, User::getUsername, User::getEmail)
       .eq(User::getDeptId, deptId);
​
List<User> users = userMapper.selectList(wrapper);

子查询

ini 复制代码
// 查有审批记录的用户
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.inSql(User::getId, "SELECT DISTINCT user_id FROM approval_record WHERE status = 'pending'");

复杂条件:AND/OR嵌套

真实业务里经常遇到这种需求:"查询(部门A 且 状态启用)或(部门B 且 角色为管理员)的用户"。Lambda写法:

perl 复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.and(w -> w.eq(User::getDeptId, 1L).eq(User::getStatus, 1))
       .or(w -> w.eq(User::getDeptId, 2L).eq(User::getRole, "ADMIN"));

生成的SQL:

ini 复制代码
SELECT * FROM sys_user 
WHERE (dept_id = 1 AND status = 1) 
   OR (dept_id = 2 AND role = 'ADMIN')

分组和聚合

sql 复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getDeptId, User::getStatus)
       .groupBy(User::getDeptId, User::getStatus)
       .having("COUNT(*) > {0}", 5);

踩过的两个坑

1. updateById 只更新非null字段

这个坑前面文章也提过。MyBatis-Plus默认策略是 FieldStrategy.NOT_NULL,null字段不更新。如果你创建了一个只含两个字段的Entity去调 updateById,其他字段不会被更新------因为null被跳过了。

解决方案:

ini 复制代码
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private Integer status;

或者用 LambdaUpdateWrapper 明确指定:

ini 复制代码
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(User::getStatus, 0)
             .eq(User::getId, userId);
userMapper.update(null, updateWrapper);

推荐后者,语义更明确。

2. like 默认不加百分号

like 方法不会自动加 %,如果不手动加就是精确匹配:

less 复制代码
// 错误:相当于 = 'xxx'
wrapper.like(User::getUsername, keyword);
​
// 正确:模糊匹配
wrapper.like(User::getUsername, "%" + keyword + "%");

用 Hutool 的 StrUtil.format("%{}%", keyword) 更简洁。

什么时候不用Lambda

  • 复杂关联查询:多表 JOIN、复杂子查询,直接用 XML 写 SQL,Lambda 不适合
  • 动态排序:orderBy 的字段来自前端传参时,只能用字符串
  • 原生函数:聚合、窗口函数等 Lambda 不支持

总结

LambdaQueryWrapper 的三个核心优势:

  1. 编译期检查 --- 字段名写错直接红,不怕重构
  2. 代码可读性好 --- User::getUsername"username" 直观
  3. 条件动态拼接 --- eq(condition, field, value) 一行搞定,告别 if-else

日常开发中 80% 的查询用它就够了,剩下 20% 复杂场景老老实实写 XML。


我正在用"一个人+AI"的模式开发微信小程序"面狮狮",微信搜索即可体验。更多实战经验:gitee.com/yao113088/j...

相关推荐
用户3169353811831 小时前
SpringBoot + Vue 项目生产环境部署完整指南
前端·后端
霸道流氓气质1 小时前
Spring 事务同步机制 —— AfterTransactionActionCollector 的异步解耦原理
java·后端·spring
Csvn2 小时前
📊 SQL 入门 Day 19:事务与隔离级别
后端·sql
面试鸭2 小时前
Anthropic 让 Claude 接了 CI 值班的第一班,人剩下的是写规则和合 PR
后端·面试
小强19882 小时前
useRef 到底用来干什么?不止获取 DOM 元素
后端
大勇前进2 小时前
React Hooks 常见 5 大坑:写完代码疯狂 Bug,避坑总结
后端
颜进强2 小时前
04 - 一张图看懂 OpenSpec 变更的一生:从创建到归档
前端·后端·ai编程
Cobyte2 小时前
Claude Code 的 Task System 的实现原理
后端·aigc·ai编程
小强19882 小时前
useEffect 完整使用指南:依赖数组、闭包陷阱、清理函数实战
后端
智驭未来掌门人2 小时前
在windows下快速搭建go2rtc流媒体平台
后端