一、为什么你需要条件构造器?

想象一下这个场景:你要从用户表里筛选出名字带”张”、年龄在20-30岁之间,并且最近一个月登录过的用户。如果手写 SQL,光是处理动态条件就得写一堆if判断,不仅麻烦还容易出错。MyBatis-Plus 的条件构造器(Wrapper)就是你的救星!

它能帮你:
✅ 用链式调用代替繁琐的 SQL 拼接
✅ 避免字段名硬编码引发的低级错误
✅ 轻松实现动态查询/更新条件


二、四大金刚:条件构造器全家桶

1. QueryWrapper:直男型操作
  • 特点:直接写字段名,适合快速上手
  • 示例:找名字叫”John”的程序员小哥
1
2
3
4
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "John")
.eq("job", "程序员");
List<User> users = userMapper.selectList(wrapper);

坑点警告:字段名写错不会报错,直到运行时爆炸!💥


2. LambdaQueryWrapper:智能防呆款
  • 特点:用 Lambda 表达式引用字段,妈妈再也不用担心我拼错字段名
  • 示例:查找年龄≥25岁的VIP用户
1
2
3
4
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.ge(User::getAge, 25)
.eq(User::getVipLevel, 1);
List<User> users = userMapper.selectList(wrapper);

为什么香?
👉 编译时检查字段名,把Bug扼杀在摇篮里
👉 代码自带注释效果,一看 User::getAge 就知道查的是年龄


3. UpdateWrapper:改数据小能手
  • 特点:专门负责数据更新,能直接设置新值
  • 示例:给技术部全员加薪500
1
2
3
4
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.eq("dept", "技术部")
.set("salary", "salary + 500");
userMapper.update(null, wrapper); // 第一个参数传null表示不基于实体对象

适用场景:批量调整数据,比如调价、状态批量变更


4. LambdaUpdateWrapper:升级版改数据
  • 特点:UpdateWrapper + Lambda 双buff加持
  • 示例:重置未激活用户的密码
1
2
3
4
5
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getStatus, 0)
.set(User::getPassword, "123456")
.set(User::getUpdateTime, LocalDateTime.now());
userMapper.update(null, wrapper);

优势对比

操作 传统写法 Lambda写法
字段名引用 手写字符串(易错) 自动推导(防呆)
改密码字段 .set("password", "123") .set(User::getPassword, "123")

三、实战教学:从青铜到王者

场景1:动态搜索过滤器

需求:前端传参可能包含 name、minAge、maxAge,要根据参数动态查询

1
2
3
4
5
6
7
public List<User> search(String name, Integer minAge, Integer maxAge) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(name), User::getName, name) // name不为空时才加条件
.gt(minAge != null, User::getAge, minAge)
.lt(maxAge != null, User::getAge, maxAge);
return userMapper.selectList(wrapper);
}

技巧
🔹 like(condition, 字段, 值) 自动跳过空条件
🔹 多个条件默认用 AND 连接


场景2:复杂更新逻辑

需求:给部门A的员工发奖金,但排除实习生

1
2
3
4
5
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getDept, "部门A")
.ne(User::getJobTitle, "实习生") // not equal
.setSql("bonus = bonus + 500"); // 直接写SQL表达式
userMapper.update(null, wrapper);

点睛之笔
👉 用 .setSql() 处理复杂计算
👉 链式调用一气呵成,比XML配置清爽10倍!


场景3:联表查询骚操作

需求:查询用户信息时带出部门名称(虽然MP不直接支持联表,但可以变通)

1
2
3
4
5
6
7
8
@Select("SELECT u.*, d.name as dept_name FROM user u LEFT JOIN dept d ON u.dept_id=d.id ${ew.customSqlSegment}")
List<User> selectUsersWithDept(@Param(Constants.WRAPPER) Wrapper<User> wrapper);

// 调用时
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getName, "张")
.eq(User::getStatus, 1);
List<User> users = userMapper.selectUsersWithDept(wrapper);

划重点
🔸 ${ew.customSqlSegment} 会自动拼接 WHERE 条件
🔸 实体类加 @TableField(exist = false) 标记非数据库字段


四、避坑指南:血泪经验总结

1. NPE 防护

错误示范

1
wrapper.eq(User::getName, params.getName()); // 如果name为null就GG

正确姿势

1
wrapper.eq(params.getName() != null, User::getName, params.getName());
2. 日期处理

坑点:直接传 new Date() 可能类型不匹配
​解决方案​​:

1
wrapper.ge(User::getCreateTime, LocalDateTime.now().minusDays(7)); // 查最近7天的数据
3. 批量操作

错误姿势:在循环里逐条更新
​正确姿势​​:

1
2
3
4
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.in(User::getId, idList) // ID列表
.set(User::getStatus, 2);
userMapper.update(null, wrapper); // 一条SQL搞定

五、性能优化秘籍

  1. 索引加持:高频查询字段记得加数据库索引
  2. 拒绝全表扫描
1
2
3
4
5
// 坏味道 ❌
wrapper.apply("YEAR(create_time) = 2023");

// 好味道 ✅
wrapper.between(User::getCreateTime, "2023-01-01", "2023-12-31");
  1. 分页优化
1
Page<User> page = new Page<>(1, 10).setSearchCount(false); // 不查总数提升速度

六、总结:怎么选最适合的Wrapper?

场景 推荐工具 原因
简单查询 LambdaQueryWrapper 避免字段名手误
复杂更新 LambdaUpdateWrapper 类型安全 + 链式更新
需要直接写字段名 QueryWrapper/UpdateWrapper 处理特殊字段或复杂表达式
动态条件 所有Wrapper的condition参数 自动跳过空条件

终极口诀
👉 能用 Lambda 就别用手写字段名
👉 条件越动态,Wrapper 越香
👉 复杂查询不要硬扛,该用XML就用XML