跳到主要内容

条件构造器

条件构造器是 MyBatis Plus 最强大的功能之一,它提供了优雅的方式来构建复杂的查询条件。本章将详细介绍各种条件构造器的使用方法。

Wrapper 家族

Wrapper 是 MyBatis Plus 条件构造器的核心,它提供了一种类型安全、优雅的方式来构建复杂的 SQL 查询条件。通过链式调用,可以直观地表达各种查询逻辑。

什么是 Wrapper?

Wrapper 本质上是一个 SQL 条件片段的构建器。当你调用 selectList(wrapper) 时,MyBatis Plus 会将 Wrapper 中构建的条件转换为 SQL 的 WHERE 子句。这种设计模式称为"构建器模式",它将复杂对象的构建过程封装起来,使调用者可以一步步构建最终的产品。

Wrapper 类型一览

MyBatis Plus 提供了多种 Wrapper 类型,适用于不同场景:

Wrapper 类说明使用场景
QueryWrapper查询条件封装简单查询,字段名字符串
LambdaQueryWrapperLambda 表达式查询条件推荐,字段类型安全
UpdateWrapper更新条件封装条件更新,字段名字符串
LambdaUpdateWrapperLambda 表达式更新条件推荐,字段类型安全
AbstractWrapper所有 Wrapper 的父类提供通用方法,不直接使用

为什么推荐使用 Lambda?

在实际开发中,强烈建议使用 Lambda 版本的 Wrapper(LambdaQueryWrapperLambdaUpdateWrapper),原因如下:

1. 避免字段名硬编码

// 不推荐:字段名字符串,容易拼写错误
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三"); // 如果字段名写错,运行时才会发现

// 推荐:Lambda 表达式,编译期检查
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getName, "张三"); // 编译期就能发现错误

2. 支持重构

当实体类字段名变更时,IDE 可以自动更新所有 Lambda 引用,而字符串字段名需要手动查找替换。

3. 代码可读性更好

// 字符串方式:字段名含义不明显
wrapper.eq("n", "张三"); // n 是什么字段?

// Lambda 方式:字段名清晰明了
lambdaWrapper.eq(User::getName, "张三");

4. 完整的 IDE 支持

IDE 可以提供自动补全、跳转定义、查找引用等功能。

比较操作

比较操作是条件构造器最基础的功能,用于构建等于、不等于、大于、小于等条件。

等于 eq

eq 方法用于构建等于条件,对应 SQL 的 = 操作符:

@Test
void testEq() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三");

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE name = '张三'
}

条件参数用法:所有 Wrapper 方法都支持第一个参数作为条件判断,只有当条件为 true 时才会添加该条件:

@Test
void testEqWithCondition() {
String name = "张三"; // 假设这个值可能为 null

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 只有 name != null 时才添加这个条件
wrapper.eq(name != null, User::getName, name);

List<User> users = userMapper.selectList(wrapper);
// 如果 name 为 null,生成 SQL: SELECT * FROM t_user
// 如果 name 不为 null,生成 SQL: SELECT * FROM t_user WHERE name = '张三'
}

这种写法在动态查询中非常有用,避免大量的 if-else 判断。

不等于 ne

ne 方法用于构建不等于条件,对应 SQL 的 <>!= 操作符:

@Test
void testNe() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.ne(User::getAge, 25);

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE age <> 25
}

大于 gt 和 大于等于 ge

@Test
void testGtGe() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// gt: greater than,对应 SQL 的 >
// ge: greater than or equal,对应 SQL 的 >=
wrapper.gt(User::getAge, 20) // age > 20
.ge(User::getAge, 18); // age >= 18

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE age > 20 AND age >= 18
// 注意:这里只是演示,实际 age > 20 已经隐含了 age >= 18
}

小于 lt 和 小于等于 le

@Test
void testLtLe() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// lt: less than,对应 SQL 的 <
// le: less than or equal,对应 SQL 的 <=
wrapper.lt(User::getAge, 30) // age < 30
.le(User::getAge, 60); // age <= 60

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE age < 30 AND age <= 60
}

区间查询 between

between 方法用于构建区间查询条件,对应 SQL 的 BETWEEN ... AND ...

@Test
void testBetween() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 查询年龄在 20 到 30 之间的用户(包含边界值)
wrapper.between(User::getAge, 20, 30);

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE age BETWEEN 20 AND 30
// 等价于: age >= 20 AND age <= 30
}

注意between 包含边界值,即 [minValue, maxValue]

排除区间 notBetween

@Test
void testNotBetween() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 查询年龄不在 20 到 30 之间的用户
wrapper.notBetween(User::getAge, 20, 30);

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE age NOT BETWEEN 20 AND 30
// 等价于: age < 20 OR age > 30
}

模糊查询

模糊查询是实际开发中最常用的查询方式之一,MyBatis Plus 提供了多种模糊查询方法。

LIKE 全匹配 like

like 方法会在值的两边添加 %,实现全模糊匹配:

@Test
void testLike() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getName, "张");

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE name LIKE '%张%'
// 可以匹配:张三、小张、张三丰、老张三 等
}

性能提示LIKE '%xxx%' 无法使用索引,数据量大时会导致全表扫描。如果只需要前缀或后缀匹配,请使用 likeRightlikeLeft

NOT LIKE notLike

@Test
void testNotLike() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.notLike(User::getName, "张");

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE name NOT LIKE '%张%'
}

左模糊 likeLeft

likeLeft 表示左边模糊,即值出现在字符串的结尾:

@Test
void testLikeLeft() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.likeLeft(User::getName, "三");

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE name LIKE '%三'
// 可以匹配:张三、李三、王三 等
// 不能匹配:张三丰、三毛 等
}

使用场景:查找以特定字符结尾的数据,如查找所有 .jpg 结尾的文件名。

右模糊 likeRight

likeRight 表示右边模糊,即值出现在字符串的开头:

@Test
void testLikeRight() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.likeRight(User::getName, "张");

List<User> users = userMapper.selectList(wrapper);
// 生成 SQL: SELECT * FROM t_user WHERE name LIKE '张%'
// 可以匹配:张三、张三丰、张小明 等
// 不能匹配:小张、老张 等
}

使用场景:前缀匹配可以使用索引,性能优于全模糊匹配。适合查找以特定前缀开头的数据,如按姓氏查找、按地区代码查找等。

模糊查询对比总结

方法SQL 效果索引使用典型场景
likeLIKE '%值%'❌ 不能全文搜索
likeLeftLIKE '%值'❌ 不能后缀匹配
likeRightLIKE '值%'✅ 可以前缀匹配、姓氏查找

范围查询

IN in

@Test
void testIn() {
List<Integer> ages = Arrays.asList(20, 25, 30);

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.in(User::getAge, ages);

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

@Test
void testInArray() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.in(User::getAge, 20, 25, 30);

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

NOT IN notIn

@Test
void testNotIn() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.notIn(User::getAge, 20, 25, 30);

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

IS NULL isNull

@Test
void testIsNull() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.isNull(User::getEmail);

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

IS NOT NULL isNotNull

@Test
void testIsNotNull() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.isNotNull(User::getEmail);

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

分组与排序

GROUP BY groupBy

@Test
void testGroupBy() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("age", "COUNT(*) as count")
.groupBy("age");

List<Map<String, Object>> result = userMapper.selectMaps(wrapper);
result.forEach(System.out::println);
}

ORDER BY orderByAsc / orderByDesc

@Test
void testOrderBy() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.orderByAsc(User::getAge)
.orderByDesc(User::getCreateTime);

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

@Test
void testOrderByWithCondition() {
boolean isAsc = true;

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.orderBy(true, isAsc, User::getAge);

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

HAVING having

@Test
void testHaving() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("age", "COUNT(*) as count")
.groupBy("age")
.having("COUNT(*) > {0}", 1);

List<Map<String, Object>> result = userMapper.selectMaps(wrapper);
}

逻辑连接

AND and

@Test
void testAnd() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三")
.and(w -> w.gt(User::getAge, 20).lt(User::getAge, 30));

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

生成的 SQL:

WHERE name = '张三' AND (age > 20 AND age < 30)

OR or

@Test
void testOr() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三")
.or()
.eq(User::getName, "李四");

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

@Test
void testOrNested() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三")
.or(w -> w.gt(User::getAge, 30).isNotNull(User::getEmail));

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

生成的 SQL:

WHERE name = '张三' OR (age > 30 AND email IS NOT NULL)

NESTED nested

@Test
void testNested() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.nested(w -> w.eq(User::getName, "张三").gt(User::getAge, 20));

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

生成的 SQL:

WHERE (name = '张三' AND age > 20)

其他条件

APPLY apply

用于拼接 SQL 片段:

@Test
void testApply() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.apply("date_format(create_time, '%Y-%m') = {0}", "2024-01");

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

@Test
void testApplyWithDate() {
String startDate = "2024-01-01";
String endDate = "2024-12-31";

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.apply("create_time BETWEEN {0} AND {1}", startDate, endDate);

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

LAST last

在 SQL 末尾追加内容:

@Test
void testLast() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.last("LIMIT 10");

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

EXISTS exists

@Test
void testExists() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.exists("SELECT 1 FROM t_order WHERE t_order.user_id = t_user.id");

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

NOT EXISTS notExists

@Test
void testNotExists() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.notExists("SELECT 1 FROM t_order WHERE t_order.user_id = t_user.id");

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

UpdateWrapper

UpdateWrapper 用于构建更新条件。

基本更新

@Test
void testUpdateWrapper() {
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getName, "张三")
.set(User::getAge, 26)
.set(User::getEmail, "[email protected]");

int result = userMapper.update(null, wrapper);
}

更新为 NULL

@Test
void testUpdateToNull() {
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getId, 1L)
.set(User::getEmail, null);

int result = userMapper.update(null, wrapper);
}

setSql 自定义 SQL

@Test
void testSetSql() {
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getId, 1L)
.setSql("age = age + 1");

int result = userMapper.update(null, wrapper);
}

条件判断

条件参数

所有方法都支持条件判断:

@Test
void testCondition() {
String name = null;
Integer minAge = 20;
Integer maxAge = null;

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(name != null, User::getName, name)
.ge(minAge != null, User::getAge, minAge)
.le(maxAge != null, User::getAge, maxAge);

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

动态条件构建

public List<User> searchUsers(String name, Integer minAge, Integer maxAge, String email) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

wrapper.like(StringUtils.isNotBlank(name), User::getName, name)
.ge(minAge != null, User::getAge, minAge)
.le(maxAge != null, User::getAge, maxAge)
.like(StringUtils.isNotBlank(email), User::getEmail, email);

return userMapper.selectList(wrapper);
}

查询字段选择

select 指定字段

@Test
void testSelectFields() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId, User::getName, User::getAge)
.gt(User::getAge, 20);

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

排除字段

@Test
void testExcludeFields() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select(User.class, info -> !info.getColumn().equals("password"));

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

实战示例

多条件组合查询

public IPage<User> searchUsers(UserQuery query, int pageNum, int pageSize) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

wrapper.like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
.eq(query.getAge() != null, User::getAge, query.getAge())
.between(query.getMinAge() != null && query.getMaxAge() != null,
User::getAge, query.getMinAge(), query.getMaxAge())
.like(StringUtils.isNotBlank(query.getEmail()), User::getEmail, query.getEmail())
.in(query.getStatusList() != null && !query.getStatusList().isEmpty(),
User::getStatus, query.getStatusList())
.orderByDesc(User::getCreateTime);

Page<User> page = new Page<>(pageNum, pageSize);
return userMapper.selectPage(page, wrapper);
}

复杂更新

public void updateUserStatus(Long userId, Integer newStatus, String operator) {
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getId, userId)
.set(User::getStatus, newStatus)
.set(User::getUpdateTime, LocalDateTime.now())
.set(User::getUpdateBy, operator)
.setSql("version = version + 1");

userMapper.update(null, wrapper);
}

统计查询

public Map<String, Object> getUserStatistics() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("COUNT(*) as total",
"AVG(age) as avgAge",
"MAX(age) as maxAge",
"MIN(age) as minAge",
"status");
wrapper.groupBy("status");

List<Map<String, Object>> stats = userMapper.selectMaps(wrapper);

Map<String, Object> result = new HashMap<>();
result.put("statistics", stats);
result.put("total", userMapper.selectCount(null));

return result;
}

小结

本章我们学习了:

  1. Wrapper 类型:QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper
  2. 比较操作:eq、ne、gt、ge、lt、le、between
  3. 模糊查询:like、likeLeft、likeRight、notLike
  4. 范围查询:in、notIn、isNull、isNotNull
  5. 分组排序:groupBy、orderByAsc、orderByDesc、having
  6. 逻辑连接:and、or、nested
  7. 高级用法:apply、last、exists、条件判断

参考资源