一、前言

       在使用 MyBatis-Plus(MP)实现复杂查询、更新、删除操作时,单纯依赖BaseMapper的默认方法无法满足多条件筛选需求。MP 提供的条件构造器(Wrapper) 是解决复杂 SQL 条件拼接的核心工具,它支持动态组装 WHERE 条件、排序、子查询等操作,无需手写 SQL 即可实现灵活的条件查询。本文将从 Wrapper 体系结构、核心实现类(QueryWrapper、LambdaQueryWrapper 等)、实战案例等维度,全面讲解条件构造器的使用方式,帮助开发者掌握 MP 的进阶核心功能。

二、Wrapper 体系结构详解

Wrapper 是 MP 中所有条件构造器的顶层抽象类,其核心子类体系如下:

类名核心作用
Wrapper条件构造抽象父类,定义了条件构造的核心规范
AbstractWrapper封装查询条件,生成 SQL 的 WHERE 子句,是 QueryWrapper/UpdateWrapper 的直接父类
QueryWrapper用于 SELECT 操作的条件构造器,侧重查询条件封装
UpdateWrapper用于 UPDATE 操作的条件构造器,支持 SET 子句和 WHERE 条件封装
AbstractLambdaWrapper基于 Lambda 语法的条件构造抽象类,避免手写字段名
LambdaQueryWrapperLambda 版 QueryWrapper,通过 Lambda 引用字段,编译期校验字段名
LambdaUpdateWrapperLambda 版 UpdateWrapper,支持 Lambda 设置 SET 字段和 WHERE 条件

核心优势

  • 无需手写 SQL,通过面向对象的方式拼接条件;
  • 支持动态条件(按需组装),适配前端多条件查询场景;
  • Lambda 版构造器避免字段名拼写错误,提升代码健壮性。

三、QueryWrapper:基础条件构造器实战

       QueryWrapper是最基础的条件构造器,通过字符串指定字段名,支持所有 SQL 查询条件的拼接,适用于简单场景。

3.1 案例 1:组装查询条件

需求:查询姓名包含 "张"、年龄大于 20 且邮箱不为空的用户。

@Test
public void testQueryWrapper1() {
    // 1. 创建QueryWrapper对象
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 2. 组装条件:like(字段名, 值)、gt(字段名, 值)、isNotNull(字段名)
    queryWrapper
        .like("name", "张")       // 姓名 LIKE '%张%'
        .gt("age", 20)            // 年龄 > 20
        .isNotNull("email");      // 邮箱 IS NOT NULL
    
    // 3. 执行查询:自动拼接WHERE条件
    // SQL:SELECT id,name,age,email FROM t_user WHERE (name LIKE ? AND age > ? AND email IS NOT NULL)
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

3.2 案例 2:组装排序条件

需求:查询所有用户,按年龄降序排序,若年龄相同则按 id 升序排序。

@Test
public void testQueryWrapper2() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 排序条件:orderByDesc(降序)、orderByAsc(升序)
    queryWrapper
        .orderByDesc("age")
        .orderByAsc("id");
    
    // SQL:SELECT id,name,age,email FROM t_user ORDER BY age DESC, id ASC
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

3.3 案例 3:组装删除条件

需求:删除邮箱为空的用户。

@Test
public void testQueryWrapper3() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 条件:email IS NULL
    queryWrapper.isNull("email");
    
    // 执行删除:条件构造器可直接作为delete方法的参数
    // SQL:DELETE FROM t_user WHERE (email IS NULL)
    int result = userMapper.delete(queryWrapper);
    System.out.println("受影响行数:" + result);
}

3.4 案例 4:组装 SELECT 子句(指定查询字段)

需求:仅查询用户的 id、name、age 字段,不查询 email。

@Test
public void testQueryWrapper4() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // select方法指定要查询的字段
    queryWrapper
        .select("id", "name", "age")
        .like("name", "张");
    
    // SQL:SELECT id,name,age FROM t_user WHERE (name LIKE ?)
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

3.5 案例 5:实现子查询

需求:查询 id 小于等于 3 的用户信息(通过子查询实现)。

@Test
public void testQueryWrapper5() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // inSql(字段名, 子查询SQL):字段值在子查询结果集中
    queryWrapper.inSql("id", "select id from t_user where id <= 3");
    
    // SQL:SELECT id,name,age,email FROM t_user WHERE (id IN (select id from t_user where id <= 3))
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

四、UpdateWrapper:更新操作的条件构造器

       UpdateWrapper专门用于 UPDATE 操作,既可以封装 WHERE 条件,也可以通过set方法指定要更新的字段,比直接使用updateById更灵活。

实战案例:多条件更新

需求:将姓名包含 "a"、年龄小于 24 或邮箱为空的用户,年龄改为 18,邮箱改为user@qcby.com

@Test
public void testUpdateWrapper() {
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    // 1. 组装SET子句
    updateWrapper
        .set("age", 18)
        .set("email", "user@qcby.com")
        // 2. 组装WHERE条件
        .like("name", "a")
        .and(i -> i.lt("age", 24).or().isNull("email")); // 嵌套条件:(age < 24 OR email IS NULL)
    
    // 执行更新:第一个参数为实体对象(可传空),第二个为条件构造器
    // SQL:UPDATE t_user SET age=?, email=? WHERE (name LIKE ? AND (age < ? OR email IS NULL))
    int result = userMapper.update(null, updateWrapper);
    System.out.println("受影响行数:" + result);
}

五、动态条件构造:condition 参数的使用

       实际开发中,查询条件往往来自前端输入(如用户名、年龄区间),这些条件是可选的(用户可能输入也可能不输入)。若直接拼接条件,会导致空条件影响查询结果,此时需通过condition参数实现动态条件组装

5.1 传统实现方式(if 判断)

@Test
public void testDynamicCondition1() {
    // 模拟前端输入:username为空(未输入),ageBegin=10,ageEnd=24
    String username = null;
    Integer ageBegin = 10;
    Integer ageEnd = 24;
    
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 手动判断条件是否有效,有效则拼接
    if (StringUtils.isNotBlank(username)) {
        queryWrapper.like("name", username);
    }
    if (ageBegin != null) {
        queryWrapper.ge("age", ageBegin); // age >= 10
    }
    if (ageEnd != null) {
        queryWrapper.le("age", ageEnd);   // age <= 24
    }
    
    // SQL:SELECT id,name,age,email FROM t_user WHERE (age >= ? AND age <= ?)
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

5.2 简化实现(带 condition 参数的重载方法)

       MP 为所有条件方法提供了带condition参数的重载版本,conditiontrue时才拼接该条件,大幅简化代码:

@Test
public void testDynamicCondition2() {
    String username = null;
    Integer ageBegin = 10;
    Integer ageEnd = 24;
    
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 第一个参数为condition,true则拼接条件
    queryWrapper
        .like(StringUtils.isNotBlank(username), "name", username)
        .ge(ageBegin != null, "age", ageBegin)
        .le(ageEnd != null, "age", ageEnd);
    
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

六、LambdaQueryWrapper:避免字段名拼写错误的最优解

       QueryWrapper通过字符串指定字段名(如"name"),若字段名拼写错误(如"nme"),仅能在运行时发现异常。LambdaQueryWrapper通过Lambda 表达式引用实体类字段(如User::getName),编译期即可校验字段名正确性,是生产环境的首选。

实战案例:LambdaQueryWrapper 动态条件查询

@Test
public void testLambdaQueryWrapper() {
    // 模拟前端输入:username="a",ageBegin=10,ageEnd=24
    String username = "a";
    Integer ageBegin = 10;
    Integer ageEnd = 24;
    
    // 创建LambdaQueryWrapper对象
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
    // 通过Lambda引用字段,无需手写字符串
    queryWrapper
        .like(StringUtils.isNotBlank(username), User::getName, username)
        .ge(ageBegin != null, User::getAge, ageBegin)
        .le(ageEnd != null, User::getAge, ageEnd);
    
    // SQL:SELECT id,name,age,email FROM t_user WHERE (name LIKE ? AND age >= ? AND age <= ?)
    List<User> userList = userMapper.selectList(queryWrapper);
    userList.forEach(System.out::println);
}

核心优势

  • 编译期校验字段名,避免运行时错误;
  • 支持 IDE 自动补全字段名,提升开发效率;
  • 实体类字段改名时,IDE 可自动重构 Lambda 表达式,降低维护成本。

七、LambdaUpdateWrapper:Lambda 版更新条件构造器

       LambdaUpdateWrapperUpdateWrapper的 Lambda 版本,同样通过 Lambda 引用字段,避免字符串拼写错误。

实战案例:LambdaUpdateWrapper 多条件更新

@Test
public void testLambdaUpdateWrapper() {
    LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper
        // SET子句:Lambda引用字段
        .set(User::getAge, 18)
        .set(User::getEmail, "user@qcby.com")
        // WHERE条件
        .like(User::getName, "a")
        // 嵌套条件:(age < 24 OR email IS NULL)
        .and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail));
    
    // 执行更新:实体对象可传空(所有更新字段通过set指定)
    int result = userMapper.update(new User(), updateWrapper);
    System.out.println("受影响行数:" + result);
}

八、Wrapper 核心方法速查表

方法名作用示例
eq等于(=)eq("age", 20) → age = 20
ne不等于(!=)ne("age", 20) → age != 20
gt大于(>)gt("age", 20) → age > 20
ge大于等于(>=)ge("age", 20) → age >= 20
lt小于(<)lt("age", 20) → age < 20
le小于等于(<=)le("age", 20) → age <= 20
like模糊查询(LIKE)like("name", "张") → name LIKE '% 张 %'
isNull为空(IS NULL)isNull("email") → email IS NULL
isNotNull不为空(IS NOT NULL)isNotNull("email") → email IS NOT NULL
inIN 查询in("id", 1,2,3) → id IN (1,2,3)
inSql子查询 INinSql("id", "select id from t_user where age<20")
orderByAsc升序排序orderByAsc("age") → ORDER BY age ASC
orderByDesc降序排序orderByDesc("age") → ORDER BY age DESC
and并且(AND)and(i -> i.lt("age",20).gt("id",1)) → AND (age<20 AND id>1)
or或者(OR)or(i -> i.eq("name","张三")) → OR (name=' 张三 ')

九、核心注意事项

  1. 字段名映射:使用非 Lambda 版构造器时,字段名需与数据库表字段一致(而非实体类属性名),若实体类属性是驼峰(如userName),表字段是下划线(user_name),需写"user_name"
  2. Lambda 表达式注意:Lambda 构造器中引用的字段必须是实体类中存在的属性,且需有对应的 getter 方法(Lombok 的@Data可自动生成);
  3. 嵌套条件优先级:使用and()/or()嵌套条件时,Lambda 表达式内的条件会优先拼接(自动加括号),避免条件优先级错误;
  4. 空值处理:动态条件务必通过condition参数判断,避免拼接null值导致 SQL 语法错误。

十、总结

关键点回顾

  1. Wrapper 是 MP 实现复杂条件查询的核心,QueryWrapper适用于简单场景,LambdaQueryWrapper通过 Lambda 避免字段名错误,是生产环境首选;
  2. 动态条件构造需使用带condition参数的方法,按需拼接条件,避免空条件影响查询结果;
  3. UpdateWrapper/LambdaUpdateWrapper适用于多条件更新,支持 SET 子句和复杂 WHERE 条件封装;
  4. 掌握 Wrapper 的核心方法(eq、like、and/or 等),可覆盖 90% 以上的复杂查询场景,无需手写 SQL。

       通过本文的讲解,相信你已掌握 MP 条件构造器的核心使用方式。条件构造器是 MP 的进阶核心,灵活运用可大幅提升复杂业务的开发效率,同时保证代码的健壮性和可维护性。

更多推荐