MyBatis-Plus条件构造器详解:告别硬编码,优雅构建动态SQL

一、什么是MyBatis-Plus的条件构造器?

在传统MyBatis中,我们经常需要在XML映射文件中编写大量的<if>标签来构建动态SQL,这不仅繁琐,而且容易出错。MyBatis-Plus的条件构造器正是为了解决这一问题而生。

核心思想: 通过链式调用的API,以Java对象的方式来表示SQL的查询条件(如WHERESELECTORDER BY等),然后由MyBatis-Plus自动将其转换为真正的SQL语句。

主要优势:

  1. 类型安全: 使用Lambda表达式时,可以避免字段名的硬编码,编译期就能发现错误。
  2. 代码简洁: 链式调用,逻辑清晰,代码简洁。
  3. 功能强大: 支持几乎所有的SQL语法,包括嵌套查询、排序、分组等。

二、核心类介绍

1. 核心类类图:

在这里插入图片描述

条件构造器的核心抽象类是Wrapper,我们最常用的是它的两个子类:

  1. QueryWrapper(查询条件构造器): 主要用于构建SELECT语句的WHERE条件。 例如:where name = ‘John’ and age > 25
  2. UpdateWrapper(更新条件构造器): 主要用于构建UPDATE语句的SET部分和WHERE条件。 例如:update user set name = ‘Tom’ where status = 0
  3. LambdaQueryWrapperLambdaUpdateWrapper(Lambda表达式版本): 功能分别与QueryWrapperUpdateWrapper对应。 最大优点: 通过Entity::getXxx方法引用来获取字段名,避免了字符串硬编码,是官方推荐的用法
2. 各实现类适用场景
实现类适用场景优势
QueryWrapper实体查询操作简单直接
UpdateWrapper实体更新操作可设置更新字段
LambdaQueryWrapper实体查询操作类型安全,推荐使用
LambdaUpdateWrapper实体更新操作类型安全,推荐使用

三、 数据准备

1. 创建表t_user
CREATE TABLE t_user (
  id bigint NOT NULL AUTO_INCREMENT,
  username varchar(32) NOT NULL,
  age int DEFAULT NULL,
  tel varchar(11) NOT NULL,
  email varchar(32) DEFAULT NULL,
  create_time datetime NOT NULL,
  status tinyint DEFAULT '1',
  PRIMARY KEY (id),
  UNIQUE KEY tel (tel)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
2. 插入数据
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (1, '张三', 18, '15100000000', 'zhangsan@163.com', '2025-08-05 17:44:56', 1);
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (2, '李四', 22, '17100000000', 'lisi@163.com', '2025-08-05 17:45:38', 1);
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (3, '王五', 35, '15800000000', 'wangwu@163.com', '2025-08-06 10:36:19', 1);
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (4, '赵六', 29, '15800000001', 'zhaoliu@163.com', '2025-08-06 10:36:19', 1);
INSERT INTO t_user (id, username, age, tel, email, create_time, status) VALUES (5, '陈七', 30, '15800000002', 'chenqi@163.com', '2025-08-06 10:36:19', 1);
3. User实体类
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.time.LocalDateTime;

@Data
public class User {

    private Long id;

    private String username;

    private int age;

    private String tel;

    private String email;
    
    private int status;

    private LocalDateTime createTime;

}

四、QueryWrapper详解与实战

1. 基础条件方法

这些方法对应SQL中的基本操作符。

方法名说明SQL等价形式
eq等于 =where name = ‘张三’
ne不等于 <>where age <> 18
gt大于 >where age > 18
ge大于等于 >=where age >= 18
lt小于 <where age < 30
le小于等于 <=where age <= 30
betweenBETWEEN 值1 AND 值2where age BETWEEN 20 AND 30
notBetweenNOT BETWEEN 值1 AND 值2where age NOT BETWEEN 20 AND 30
likeLIKE ‘%值%’where name like ‘%张%’
likeLeftLIKE ‘%值’where name like ‘%三’
likeRightLIKE ‘值%’where name like ‘张%’
isNull字段 IS NULLwhere email IS NULL
isNotNull字段 IS NOT NULLwhere email IS NOT NULL
in字段 IN (v0, v1, …)where id in (1, 2, 3)
notIn字段 NOT IN (v0, v1, …)where id not in (1, 2, 3)
1.1. 等值、范围查询
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	// SQL: username = '张三'
    queryWrapper.eq("username", "张三"); 

    // 不等查询
	// SQL: status <> 0
    queryWrapper.ne("status", 0);

    // 范围查询
	// SQL: age BETWEEN 18 AND 25
    queryWrapper.between("age", 18, 25);

    // 模糊查询
	// SQL: name LIKE '%张%'
    queryWrapper.like("username", "张");
    List<User> users = userMapper.selectList(queryWrapper);
1.2. 空值检查、IN查询
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// SQL: email IS NULL
queryWrapper.isNull("email");   
// SQL: create_time IS NOT NULL
queryWrapper.isNotNull("create_time"); 
// SQL: id IN (1,2,3)
queryWrapper.in("id", Arrays.asList(1, 2, 3)); 
// SQL: status NOT IN (0,2)
queryWrapper.notIn("status", Arrays.asList(0, 2)); 

2. 复杂查询
方法名说明SQL等价形式
and默认是AND,可省略。用于连接条件where (age > 25 AND name = ‘张’)
or使用OR连接条件where (age > 25 OR status = 1)
nested嵌套条件,用于复杂的括号组合where (age > 25 AND (name = ‘张’ OR name = ‘王’))
orderByAsc按字段升序排序ORDER BY age ASC
orderByDesc按字段降序排序ORDER BY age DESC
groupBy按字段分组GROUP BY department
having分组后过滤GROUP BY ... HAVING count(*) > 1
select指定查询的列SELECT id, name
2.1 逻辑操作与嵌套查询
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // AND 连接(默认,可省略)
	// SQL: age > 18 AND age <= 65 AND status = 1
    queryWrapper.gt("age", 18)
    	.le("age", 30)
    	.eq("status", 1); 

    // OR 连接
	// SQL: username = '张三' OR username = '李四'
    queryWrapper.eq("username", "张三").or().eq("username", "李四");

    // 复杂嵌套条件
	// SQL: ( age > 20 OR email IS NULL ) OR ( age < 10 AND status = 1 )
    queryWrapper.and(wp -> wp.gt("age", 20).or().isNull("email"))
    	.or(wp -> wp.lt("age", 10).and(wp2 -> wp2.eq("status", 1)));

2.2 排序、分组、分页
	// 选择特定字段(避免SELECT *)
    // SELECT id, username, age
    queryWrapper.select("id", "username", "age");

	// 排序
    // ORDER BY age ASC, id ASC
    queryWrapper.orderByAsc("age", "id");
    // ORDER BY create_time DESC
    queryWrapper.orderByDesc("create_time");

    // 分组
    // GROUP BY status
    queryWrapper.groupBy("status");
    // HAVING count(*) > 1
    queryWrapper.having("count(*) > 1");

五、LambdaQueryWrapper:类型安全的写法

1. 基础用法对比
    // 传统QueryWrapper - 字段名硬编码(不推荐)
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("username", "张三")
        .lt("age", 25)
        .select("id", "username", "age");

    // LambdaQueryWrapper - 类型安全(推荐)
    LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
    lambdaWrapper.eq(User::getUsername, "张三")
        .lt(User::getAge, 25)
        .select(User::getId, User::getUsername, User::getAge);

优势:

  • 编译期检查,避免字段名拼写错误
  • 支持IDE智能提示和重构
  • 代码可读性更强
2. 复杂Lambda查询示例
	@Test
    public void testLambdaWrapper() {

        // 复杂条件查询
        LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
        lambdaWrapper.gt(User::getAge, 18)
                .and(wp -> wp.eq(User::getStatus, 1).or().eq(User::getStatus, 2))
                .likeRight(User::getUsername, "王")
                .orderByDesc(User::getCreateTime)
                .last("LIMIT 2"); // 自定义SQL拼接(谨慎使用)

        List<User> users = userMapper.selectList(lambdaWrapper);
    }

运行单元测试结果如下:

在这里插入图片描述


六、UpdateWrapper更新操作详解

UpdateWrapper除了能设置WHERE条件,还能设置要更新的字段。

1. 基本更新操作
    // 1. 使用UpdateWrapper设置更新字段和条件
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.set("age", 30)        // SET age = 30
        .eq("id", 1);          // WHERE id = 1
    userMapper.update(updateWrapper);

    // 2. 使用LambdaUpdateWrapper(推荐)
    LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
    lambdaUpdateWrapper.set(User::getAge, 30)
        .eq(User::getId, 1);
    userMapper.update(lambdaUpdateWrapper);

    // 3. 实体+Wrapper组合更新
    User updateUser = new User();
    updateUser.setAge(30);

    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
    wrapper.eq(User::getId, 1);
    // 将id=1的用户年龄更新为30
    userMapper.update(updateUser, wrapper);
2. 特殊更新操作
    // 数学运算更新
    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
    wrapper.setSql("age = age + 1")  // 年龄+1
        .eq(User::getStatus, 1);

    // 批量更新符合条件的数据
    userMapper.update(wrapper);

七、最佳实践与避坑指南

  1. 首选 Lambda 表达式:

    • 硬编码(不推荐): queryWrapper.eq("username", "张三”)。如果数据库字段名改了,这里容易出错。
    • Lambda(强烈推荐): lambdaQueryWrapper.eq(User::getUsername, "张三”)。类型安全,编译期检查,支持IDE的代码重构。
  2. 避免在 Wrapper中写死数据库表字段名:

    始终通过实体类的属性映射,这样即使数据库字段名发生变化(如从user_name改为username),也只需修改实体类的注解即可。

  3. 注意 null值的处理:

    • 条件方法(如eq)传入的值为null时,默认生成的SQL是字段 = null,这通常不是我们想要的(应该用isNull)。
    • 可以使用condition参数进行判断。
    String name = null; // 可能为null的查询参数
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    // 传统if判断
    // if (name != null) {
    //     wrapper.eq(User::getName, name);
    // }
    
    // 更优雅的方式:使用condition参数
    // 只有当name != null时,该条件才会被拼接
    wrapper.eq(name != null, User::getUsername, name); 
    
  4. 复杂 SQL 的取舍: 对于极其复杂的多表关联查询或动态SQL,条件构造器可能会变得难以维护。此时,退回到使用MyBatis原生的XML映射文件可能是更明智的选择。

八、总结

MyBatis-Plus的条件构造器是一个极其强大且灵活的工具,它极大地简化了动态SQL的编写,提升了开发效率和代码的可读性、可维护性。掌握QueryWrapperUpdateWrapper以及它们的Lambda版本,你就能在项目中游刃有余地处理各种数据查询和更新场景。虽然MyBatis-Plus的条件构造器的功能强大,好用,但我们也需要记住一大原则:简单查询用Wrapper,复杂查询用XML,只有合理选择才能真正解决我们的问题!

;


4. **复杂 SQL 的取舍:** 对于极其复杂的多表关联查询或动态SQL,条件构造器可能会变得难以维护。此时,退回到使用MyBatis原生的XML映射文件可能是更明智的选择。

### 八、总结

MyBatis-Plus的条件构造器是一个极其强大且灵活的工具,它极大地简化了动态SQL的编写,提升了开发效率和代码的可读性、可维护性。掌握`QueryWrapper`、`UpdateWrapper`以及它们的Lambda版本,你就能在项目中游刃有余地处理各种数据查询和更新场景。虽然MyBatis-Plus的条件构造器的功能强大,好用,但我们也需要记住一大原则:简单查询用Wrapper,复杂查询用XML,只有合理选择才能真正解决我们的问题!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值