一、条件构造器

MyBatis-Plus(MP)的核心优势之一,就是通过 Wrapper 体系(尤其是 QueryWrapper 和 UpdateWrapper)提供了强大、灵活且类型安全的动态 SQL 构建能力,特别擅长处理复杂的 WHERE 条件

1-1、AbstractWrapper

1、QueryWrapper && UpdateWrapper

QueryWrapper 在where条件的基础上,可以控制多加了select 可选的查询字段。

默认是select *

(1)QueryWrapper 示例:

(2)updateWrapper示例:

UpdateWrapper 第一个参数传 null 是因为你不提供实体对象;

如果你传了实体,MP 会把实体中非空字段也加入 SET,可能造成意料之外的覆盖

⚠️ 注意事项

如果你有实体对象,并且想更新它的某些字段 + 按主键更新,也可以用:

User user = new User();
user.setId(1001);
user.setNickname("新昵称");

userMapper.updateById(user); // 更简洁

这种情况不需要 UpdateWrapper

小结:

场景 推荐方式
按主键更新几个字段 updateById(entity)
非主键条件更新 update(null, updateWrapper)
动态条件/字段更新 UpdateWrapperLambdaUpdateWrapper

2、AbstractLambdaWrapper

以上的queryWrapper 和 updateWrapper 字段名都是写死的,硬编码。

二、小结

三、QueryWrapper VS UpdateWrapper

3-1、一句话区分

Wrapper 用途 核心能力
QueryWrapper 查询、删除 只构造 WHERE 条件
UpdateWrapper 更新 构造 WHERE 条件 + SET 更新内容

3-2、继承关系

AbstractWrapper  ← 所有 Wrapper 的基类(构造 WHERE)
    ├── QueryWrapper      ← 只负责 WHERE
    ├── UpdateWrapper     ← 负责 WHERE + SET
    └── LambdaQueryWrapper / LambdaUpdateWrapper  ← 各自对应的 Lambda 版本

UpdateWrapper 比 QueryWrapper 多了一个 set() 方法,用来指定要更新的字段和值。


3-3、代码对比:同一个业务场景

场景:把"年龄大于 30 且状态为禁用"的用户,状态改为启用。

❌ 错误:用 QueryWrapper 做更新

// QueryWrapper 只能构造 WHERE,没法指定 SET 内容!
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 30).eq("status", 0);

// update() 方法需要传入一个实体对象作为 SET 内容
User user = new User();
user.setStatus(1);  // 要更新的字段值放在实体里

userMapper.update(user, queryWrapper);

缺点:

  • 实体 user 中其他字段如果为 null,配合 not_null 策略会被忽略,但你得new一个对象

  • 逻辑上"条件和更新内容"分离,不够直观


✅ 正确:用 UpdateWrapper 做更新

UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();

// WHERE 条件:年龄 > 30 且 状态 = 0
updateWrapper.gt("age", 30).eq("status", 0);

// SET 内容:直接在这里指定!
updateWrapper.set("status", 1);
// 还可以同时更新多个字段
updateWrapper.set("update_time", new Date());

userMapper.update(null, updateWrapper);

优点:

  • 条件和更新内容写在一起,一目了然

  • 第一个参数传 null,完全不需要 new 实体对象

  • 可以更新为 nullset("phone", null)),不受实体 null 策略影响


3-4、什么时候用哪个?(决策树)

你要做什么操作?
│
├─→ 查询(SELECT)─────────────────→ QueryWrapper / LambdaQueryWrapper
│     例:list(wrapper)、getOne(wrapper)、count(wrapper)
│
├─→ 删除(DELETE)─────────────────→ QueryWrapper / LambdaQueryWrapper
│     例:remove(wrapper)
│     (删除只需要条件,不需要 SET)
│
└─→ 更新(UPDATE)─────────────────→ UpdateWrapper / LambdaUpdateWrapper
      例:update(null, wrapper)
      (更新既需要 WHERE 条件,又需要 SET 内容)

3-5、Lambda 版本(推荐实际使用)

上面用的是字符串硬编码列名,容易写错。实际开发强烈推荐 Lambda 版本

LambdaQueryWrapper —— 查询/删除

// 查询:名字包含"张"且状态启用的用户
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.like(User::getUsername, "张")
   .eq(User::getStatus, 1);

List<User> list = userMapper.selectList(lqw);

LambdaUpdateWrapper —— 更新

// 更新:id=1 的用户,只改邮箱和更新时间
LambdaUpdateWrapper<User> luw = new LambdaUpdateWrapper<>();
luw.eq(User::getId, 1L)
   .set(User::getEmail, "new@qq.com")
   .set(User::getUpdateTime, new Date());

userMapper.update(null, luw);

Lambda 的好处: User::getUsername 是编译期检查的,列名写错直接编译报错,不会等到运行时才暴露。


3-6、一个完整的实战对比

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    // ========== 查询:用 QueryWrapper ==========
    public List<User> getActiveUsersByAge(Integer minAge) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(User::getAge, minAge)      // WHERE age >= ?
               .eq(User::getStatus, 1);        // AND status = 1
        
        return userMapper.selectList(wrapper);
    }

    // ========== 删除:用 QueryWrapper ==========
    public boolean deleteInactiveUsers() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getStatus, 0)         // WHERE status = 0
               .lt(User::getCreateTime, 
                   LocalDateTime.now().minusYears(1)); // AND create_time < '一年前'
        
        return userMapper.delete(wrapper) > 0;
    }

    // ========== 更新:用 UpdateWrapper ==========
    public boolean banUsersByAge(Integer maxAge) {
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.le(User::getAge, maxAge)       // WHERE age <= ?
               .eq(User::getStatus, 1)          // AND status = 1
               .set(User::getStatus, 0)         // SET status = 0
               .set(User::getUpdateTime, 
                    LocalDateTime.now());       // SET update_time = now()
        
        return userMapper.update(null, wrapper) > 0;
    }
}

3-7、容易踩的坑

说明
UpdateWrapper 做查询 语法上可行(因为继承了 AbstractWrapper),但语义不对,别这么干
QueryWrapper 做更新但没传实体 update(null, queryWrapper) → SET 子句为空,SQL 报错
UpdateWrapper 传了实体又用了 set() 两者会合并,实体和 set() 都会进 SET 子句,注意别重复
忘记 update(null, wrapper) 第一个参数 用 UpdateWrapper 时第一个参数固定传 null

3-8、速记口诀

查删用 Query,更新用 Update。 Query 只管 WHERE,Update 还能 SET。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐