详解 MyBatis-Plus 框架中 QueryWrapper 类
QueryWrapper
- 一、 QueryWrapper 的概念
- 为什么需要 QueryWrapper?
- 二、 QueryWrapper 的基本使用
- 1. 创建 QueryWrapper 实例
- 2. 添加查询条件
- 3. 执行查询
- 三、 QueryWrapper 的常见方法
- 1. 基本条件方法
- 1.1 `eq` - 等于
- 1.2 `ne` - 不等于
- 1.3 `gt` - 大于
- 1.4 `ge` - 大于等于
- 1.5 `lt` - 小于
- 1.6 `le` - 小于等于
- 1.7 `between` - 在某个范围内
- 1.8 `notBetween` - 不在某个范围内
- 1.9 `like` - 模糊匹配
- 1.10 `notLike` - 不包含某个字符串
- 1.11 `likeLeft` - 左模糊匹配
- 1.12 `likeRight` - 右模糊匹配
- 1.13 `in` - 在某个集合中
- 1.14 `notIn` - 不在某个集合中
- 1.15 `isNull` - 为空
- 1.16 `isNotNull` - 不为空
- 2. 逻辑运算方法
- 2.1 `and` - AND 条件
- 2.2 `or` - OR 条件
- 2.3 `nested` - 嵌套条件
- 3. 排序方法
- 3.1 `orderByAsc` - 升序排序
- 3.2 `orderByDesc` - 降序排序
- 3.3 `orderBy` - 自定义排序
- 4. 选择字段
- 4.1 `select` - 指定查询字段
- 5. 其他实用方法
- 5.1 `last` - 自定义 SQL 片段
- 5.2 `apply` - 动态 SQL 片段
- 6. 综合示例
- 四、 QueryWrapper 与其他 Wrapper 的区别
- LambdaQueryWrapper 的优势
- 五、 应用例子
- 1. 简单查询
- 2. 复杂条件查询
- 3. 动态查询
- 4. 使用 LambdaQueryWrapper
- 六、 QueryWrapper 的注意事项
- 七、 总结
一、 QueryWrapper 的概念
QueryWrapper 是 MyBatis-Plus 框架中的一个核心类,用于以对象化的方式构建 SQL 查询条件。MyBatis-Plus 是一个基于 MyBatis 的增强工具,旨在简化数据库操作,而 QueryWrapper 正是其提供的一个查询构造器。通过链式调用,开发者可以灵活、直观地构造复杂的 SQL 查询,而无需手动编写 SQL 语句。
为什么需要 QueryWrapper?
在传统的 MyBatis 中,查询条件通常需要在 XML 文件中编写 SQL,或者通过字符串拼接生成动态 SQL。这种方式在处理复杂或动态查询时容易出错,且代码可读性较差。QueryWrapper 的出现解决了这些问题,它通过方法调用构建查询条件,不仅提高了代码的安全性(避免 SQL 注入),还让查询逻辑更加清晰。
二、 QueryWrapper 的基本使用
1. 创建 QueryWrapper 实例
QueryWrapper 通常与实体类绑定,通过泛型指定。例如,对于一个 User
实体类,可以这样创建:
QueryWrapper<User> wrapper = new QueryWrapper<>();
这里的 User
就是我们的实体类,代表数据库中的一张表。
2. 添加查询条件
QueryWrapper 提供了丰富的条件构造方法,例如:
eq
:等于ne
:不等于gt
:大于like
:模糊匹配in
:在某个集合中
这些方法支持链式调用,可以轻松组合多个条件。例如:
wrapper.eq("age", 18).like("name", "John");
3. 执行查询
构造好 QueryWrapper 后,将其传递给 MyBatis-Plus 的 Mapper 接口方法(如 selectList
、selectOne
等)来执行查询。例如:
List<User> users = userMapper.selectList(wrapper);
这样就会返回满足条件的用户列表。
三、 QueryWrapper 的常见方法
1. 基本条件方法
这些方法用于指定字段与值之间的关系,通常以数据库列名和对应的值作为参数,是构建查询条件的基础。
1.1 eq
- 等于
- 功能:指定某个字段等于给定的值。
- 参数:
column
:字段名(字符串类型,对应数据库列名)。value
:要匹配的值(可以是任意类型,如整数、字符串等)。
- 使用场景:适用于需要精确匹配的查询,例如查找特定年龄或 ID 的记录。
- 示例:
wrapper.eq("age", 18); // WHERE age = 18
- 说明:生成的 SQL 会将字段与值用
=
连接,适用于简单的相等条件。
1.2 ne
- 不等于
- 功能:指定某个字段不等于给定的值。
- 参数:
column
:字段名。value
:不匹配的值。
- 使用场景:用于排除特定值的记录,例如查找年龄不是 18 的用户。
- 示例:
wrapper.ne("age", 18); // WHERE age != 18
- 说明:生成的 SQL 使用
!=
或<>
表示不等于。
1.3 gt
- 大于
- 功能:指定某个字段大于给定的值。
- 参数:
column
:字段名。value
:比较的下限值。
- 使用场景:查询字段值超过某个阈值的记录,例如查找年龄大于 18 的用户。
- 示例:
wrapper.gt("age", 18); // WHERE age > 18
- 说明:适用于数值型字段的范围查询。
1.4 ge
- 大于等于
- 功能:指定某个字段大于或等于给定的值。
- 参数:
column
:字段名。value
:比较的下限值。
- 使用场景:查询字段值达到或超过某个值的记录,例如查找年龄 18 或以上的用户。
- 示例:
wrapper.ge("age", 18); // WHERE age >= 18
1.5 lt
- 小于
- 功能:指定某个字段小于给定的值。
- 参数:
column
:字段名。value
:比较的上限值。
- 使用场景:查询字段值低于某个阈值的记录,例如查找年龄小于 30 的用户。
- 示例:
wrapper.lt("age", 30); // WHERE age < 30
1.6 le
- 小于等于
- 功能:指定某个字段小于或等于给定的值。
- 参数:
column
:字段名。value
:比较的上限值。
- 使用场景:查询字段值不超过某个值的记录,例如查找年龄 30 或以下的用户。
- 示例:
wrapper.le("age", 30); // WHERE age <= 30
1.7 between
- 在某个范围内
- 功能:指定某个字段的值在给定的闭区间内(包含边界)。
- 参数:
column
:字段名。val1
:范围下限。val2
:范围上限。
- 使用场景:查询字段值在特定范围内的记录,例如查找年龄在 18 到 30 之间的用户。
- 示例:
wrapper.between("age", 18, 30); // WHERE age BETWEEN 18 AND 30
- 说明:等价于
ge("age", 18).le("age", 30)
。
1.8 notBetween
- 不在某个范围内
- 功能:指定某个字段的值不在给定的闭区间内。
- 参数:
column
:字段名。val1
:范围下限。val2
:范围上限。
- 使用场景:查询字段值在特定范围外的记录,例如查找年龄不在 18 到 30 之间的用户。
- 示例:
wrapper.notBetween("age", 18, 30); // WHERE age NOT BETWEEN 18 AND 30
1.9 like
- 模糊匹配
- 功能:指定某个字段包含给定的字符串(前后自动添加通配符
%
)。 - 参数:
column
:字段名。value
:要匹配的子字符串。
- 使用场景:查询字段中包含某个子串的记录,例如查找名字中包含 “John” 的用户。
- 示例:
wrapper.like("name", "John"); // WHERE name LIKE '%John%'
- 说明:
%
表示任意字符的通配符,适合文本搜索。
1.10 notLike
- 不包含某个字符串
- 功能:指定某个字段不包含给定的字符串。
- 参数:
column
:字段名。value
:不匹配的子字符串。
- 使用场景:查询字段中不包含某个子串的记录,例如查找名字不包含 “John” 的用户。
- 示例:
wrapper.notLike("name", "John"); // WHERE name NOT LIKE '%John%'
1.11 likeLeft
- 左模糊匹配
- 功能:指定某个字段以给定的字符串结尾(左侧添加
%
)。 - 参数:
column
:字段名。value
:结尾字符串。
- 使用场景:查询字段以特定字符串结尾的记录,例如查找名字以 “son” 结尾的用户。
- 示例:
wrapper.likeLeft("name", "son"); // WHERE name LIKE '%son'
1.12 likeRight
- 右模糊匹配
- 功能:指定某个字段以给定的字符串开头(右侧添加
%
)。 - 参数:
column
:字段名。value
:开头字符串。
- 使用场景:查询字段以特定字符串开头的记录,例如查找名字以 “Jo” 开头的用户。
- 示例:
wrapper.likeRight("name", "Jo"); // WHERE name LIKE 'Jo%'
1.13 in
- 在某个集合中
- 功能:指定某个字段的值在给定的集合内。
- 参数:
column
:字段名。value
:集合(如List
或数组)。
- 使用场景:查询字段值在特定集合中的记录,例如查找 ID 为 1、2、3 的用户。
- 示例:
wrapper.in("id", Arrays.asList(1, 2, 3)); // WHERE id IN (1, 2, 3)
- 说明:支持动态传入集合,适合批量查询。
1.14 notIn
- 不在某个集合中
- 功能:指定某个字段的值不在给定的集合内。
- 参数:
column
:字段名。value
:集合。
- 使用场景:查询字段值不在特定集合中的记录,例如查找 ID 不在 1、2、3 中的用户。
- 示例:
wrapper.notIn("id", Arrays.asList(1, 2, 3)); // WHERE id NOT IN (1, 2, 3)
1.15 isNull
- 为空
- 功能:指定某个字段为
NULL
。 - 参数:
column
:字段名。
- 使用场景:查询字段值为
NULL
的记录,例如查找没有邮箱的用户。 - 示例:
wrapper.isNull("email"); // WHERE email IS NULL
1.16 isNotNull
- 不为空
- 功能:指定某个字段不为
NULL
。 - 参数:
column
:字段名。
- 使用场景:查询字段值不为
NULL
的记录,例如查找有邮箱的用户。 - 示例:
wrapper.isNotNull("email"); // WHERE email IS NOT NULL
2. 逻辑运算方法
这些方法用于组合多个条件,支持 AND
和 OR
逻辑,增强查询的灵活性。
2.1 and
- AND 条件
- 功能:在当前条件后添加
AND
逻辑,连接另一个条件。 - 参数:
- Lambda 表达式或
Consumer<QueryWrapper>
,用于指定子条件。
- Lambda 表达式或
- 使用场景:组合多个条件时,使用
AND
连接,例如查找年龄为 18 且名字包含 “John” 的用户。 - 示例:
wrapper.eq("age", 18).and(w -> w.like("name", "John")); // WHERE age = 18 AND name LIKE '%John%'
- 说明:Lambda 表达式允许嵌套条件,提高可读性。
2.2 or
- OR 条件
- 功能:在当前条件后添加
OR
逻辑,连接另一个条件。 - 参数:无(链式调用)。
- 使用场景:组合多个条件时,使用
OR
连接,例如查找年龄为 18 或 20 的用户。 - 示例:
wrapper.eq("age", 18).or().eq("age", 20); // WHERE age = 18 OR age = 20
2.3 nested
- 嵌套条件
- 功能:在条件中嵌套子条件,通常与括号一起使用。
- 参数:
- Lambda 表达式,定义嵌套的子条件。
- 使用场景:构建复杂的嵌套查询,例如查找年龄为 18 或 20 的用户,且名字包含 “John”。
- 示例:
wrapper.nested(w -> w.eq("age", 18).or().eq("age", 20)).like("name", "John"); // WHERE (age = 18 OR age = 20) AND name LIKE '%John%'
- 说明:嵌套条件会被括号包裹,便于控制逻辑优先级。
3. 排序方法
这些方法用于指定查询结果的排序方式。
3.1 orderByAsc
- 升序排序
- 功能:按指定字段升序排序。
- 参数:
columns
:一个或多个字段名。
- 使用场景:需要按字段从小到大排序,例如按年龄升序排列用户。
- 示例:
wrapper.orderByAsc("age"); // ORDER BY age ASC
3.2 orderByDesc
- 降序排序
- 功能:按指定字段降序排序。
- 参数:
columns
:一个或多个字段名。
- 使用场景:需要按字段从大到小排序,例如按年龄降序排列用户。
- 示例:
wrapper.orderByDesc("age"); // ORDER BY age DESC
3.3 orderBy
- 自定义排序
- 功能:根据条件动态决定是否排序,以及升序或降序。
- 参数:
condition
:布尔值,是否应用排序。isAsc
:布尔值,是否升序。columns
:字段名。
- 使用场景:动态控制排序逻辑,例如根据参数决定是否按年龄排序。
- 示例:
wrapper.orderBy(true, true, "age"); // ORDER BY age ASC
4. 选择字段
这些方法用于控制查询返回的列,优化数据传输。
4.1 select
- 指定查询字段
- 功能:选择需要查询的字段,而不是默认返回所有字段。
- 参数:
columns
:一个或多个字段名。
- 使用场景:只需要部分字段时,减少不必要的数据返回,例如只查询用户的 ID 和姓名。
- 示例:
wrapper.select("id", "name"); // SELECT id, name FROM user
5. 其他实用方法
5.1 last
- 自定义 SQL 片段
- 功能:在 SQL 末尾追加自定义片段。
- 参数:
sql
:SQL 字符串。
- 使用场景:需要添加特殊 SQL 语句,例如分页限制。
- 示例:
wrapper.last("LIMIT 10"); // 在 SQL 末尾添加 LIMIT 10
5.2 apply
- 动态 SQL 片段
- 功能:添加动态 SQL 片段,支持占位符参数。
- 参数:
sql
:SQL 片段。args
:占位符参数。
- 使用场景:需要动态插入复杂条件,例如日期格式化查询。
- 示例:
wrapper.apply("date_format(create_time, '%Y-%m-%d') = {0}", "2023-01-01"); // WHERE date_format(create_time, '%Y-%m-%d') = '2023-01-01'
6. 综合示例
假设有一个 User
表,包含 id
、name
、age
和 email
字段,以下是一个综合使用常见方法的查询:
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("age", 18) // 年龄 >= 18.like("name", "John") // 名字包含 "John".isNotNull("email") // 邮箱不为空.orderByDesc("age") // 按年龄降序.select("id", "name", "age"); // 只查询 id、name、age 字段
List<User> users = userMapper.selectList(wrapper);
生成 SQL:
SELECT id, name, age
FROM user
WHERE age >= 18
AND name LIKE '%John%'
AND email IS NOT NULL
ORDER BY age DESC
四、 QueryWrapper 与其他 Wrapper 的区别
MyBatis-Plus 提供了多种 Wrapper 类,各有侧重:
QueryWrapper
:专注于构建 SELECT 查询的条件。UpdateWrapper
:用于构建 UPDATE 语句,支持条件和 SET 子句。UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", 1).set("age", 20); // UPDATE user SET age = 20 WHERE id = 1
LambdaQueryWrapper
:QueryWrapper 的 Lambda 版本,使用 Lambda 表达式指定字段。LambdaUpdateWrapper
:UpdateWrapper 的 Lambda 版本。
LambdaQueryWrapper 的优势
LambdaQueryWrapper 使用 Lambda 表达式,避免了硬编码字段名。例如:
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getAge, 18); // 使用方法引用指定字段
这种方式更安全,因为字段名由编译器检查,避免拼写错误。
五、 应用例子
1. 简单查询
查询年龄等于 18 岁的用户:
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("age", 18);
List<User> users = userMapper.selectList(wrapper);
生成的 SQL:
SELECT * FROM user WHERE age = 18
2. 复杂条件查询
查询年龄在 18 到 30 岁之间,且名字包含 “John” 的用户,按年龄降序排序:
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 18, 30).like("name", "John").orderByDesc("age");
List<User> users = userMapper.selectList(wrapper);
生成的 SQL:
SELECT * FROM user WHERE age BETWEEN 18 AND 30 AND name LIKE '%John%' ORDER BY age DESC
3. 动态查询
根据传入的参数动态构建条件:
public List<User> searchUsers(Integer minAge, Integer maxAge, String name) {QueryWrapper<User> wrapper = new QueryWrapper<>();if (minAge != null) {wrapper.ge("age", minAge);}if (maxAge != null) {wrapper.le("age", maxAge);}if (name != null && !name.isEmpty()) {wrapper.like("name", name);}return userMapper.selectList(wrapper);
}
这段代码使用了MyBatis Plus的QueryWrapper来构建动态查询条件。根据参数的值,会生成不同的SQL语句。
-
默认情况(所有参数都为null或空):
searchUsers(null, null, null);
生成的SQL语句将不会有任何where条件:
SELECT * FROM user
-
只有minAge不为null:
searchUsers(18, null, null);
生成的SQL语句将包含最小年龄条件:
SELECT * FROM user WHERE age >= 18
-
只有maxAge不为null:
searchUsers(null, 30, null);
生成的SQL语句将包含最大年龄条件:
SELECT * FROM user WHERE age <= 30
-
只有name不为null且不为空字符串:
searchUsers(null, null, "张三");
生成的SQL语句将包含名字模糊查询条件(使用LIKE):
SELECT * FROM user WHERE name LIKE '%张三%'
-
组合条件(minAge和maxAge都不为null):
searchUsers(18, 30, null);
生成的SQL语句将包含年龄范围条件:
SELECT * FROM user WHERE age >= 18 AND age <= 30
-
组合条件(minAge和name都不为null):
searchUsers(18, null, "张三");
生成的SQL语句将包含最小年龄和名字模糊查询条件:
SELECT * FROM user WHERE age >= 18 AND name LIKE '%张三%'
-
组合条件(maxAge和name都不为null):
searchUsers(null, 30, "张三");
生成的SQL语句将包含最大年龄和名字模糊查询条件:
SELECT * FROM user WHERE age <= 30 AND name LIKE '%张三%'
-
所有参数都不为null或空:
searchUsers(18, 30, "张三");
生成的SQL语句将包含所有条件:
SELECT * FROM user WHERE age >= 18 AND age <= 30 AND name LIKE '%张三%'
4. 使用 LambdaQueryWrapper
查询年龄等于 18 且名字包含 “John” 的用户:
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getAge, 18).like(User::getName, "John");
List<User> users = userMapper.selectList(lambdaWrapper);
六、 QueryWrapper 的注意事项
- 字段名问题:QueryWrapper 中的字段名应与数据库列名一致,而不是实体类的属性名(除非通过
@TableField
注解指定了映射)。 - NULL 值处理:使用
isNull
或isNotNull
方法来明确处理 NULL 值。 - 性能考虑:对于非常复杂的查询,QueryWrapper 可能生成较长的 SQL,建议在性能敏感场景下优化或手动编写 SQL。
- 版本兼容性:MyBatis-Plus 的不同版本可能对方法有所调整,建议参考官方文档。
七、 总结
QueryWrapper 是 MyBatis-Plus 中一个强大且灵活的工具,通过链式调用和对象化的方式,开发者可以轻松构建 SQL 查询条件。它不仅提高了开发效率,还增强了代码的可读性和安全性。无论是简单查询还是复杂动态条件,QueryWrapper 都能胜任。如果我们需要更安全的字段引用,可以选择 LambdaQueryWrapper。