MyBatis-Plus作为MyBatis的增强工具,提供了强大的查询构造器功能。其中LambdaQueryWrapper
和QueryWrapper
是最常用的两种查询条件封装方式。本文将深入解析它们的用法,并通过实际案例展示如何选择最适合的查询构造器。
一、QueryWrapper:传统条件构造器
1. 基本用法
QueryWrapper
是MyBatis-Plus最早提供的条件构造器,通过字符串指定字段名:
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三")
.between("age", 20, 30)
.like("email", "@example.com");
2. 常用API方法
方法名 | 说明 | 示例 |
---|---|---|
eq | 等于 = | eq("name", "张三") |
ne | 不等于 != | ne("status", 0) |
gt | 大于 > | gt("age", 18) |
ge | 大于等于 >= | ge("score", 60) |
lt | 小于 < | lt("create_time", LocalDateTime.now()) |
le | 小于等于 <= | le("balance", 1000) |
between | BETWEEN 值1 AND 值2 | between("age", 18, 30) |
like | LIKE '%值%' | like("name", "张") |
orderByAsc | 升序排序 | orderByAsc("create_time") |
3. 复杂查询示例
// 多条件组合查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "name", "age")
.eq("dept_id", 2)
.and(qw -> qw.gt("age", 20).or().isNotNull("email"))
.orderByDesc("create_time");
List<User> users = userMapper.selectList(wrapper);
二、LambdaQueryWrapper:类型安全条件构造器
1. 基本用法
LambdaQueryWrapper
通过方法引用指定字段,避免字符串硬编码:
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三")
.between(User::getAge, 20, 30)
.like(User::getEmail, "@example.com");
2. 核心优势
- 类型安全:编译时检查字段名是否正确
- IDE支持:自动补全和方法跳转
- 重构友好:字段名修改自动同步到查询条件
- 代码可读性:更接近自然语言的表达方式
3. 复杂查询示例
// 嵌套查询+多条件组合
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId, User::getName, User::getAge)
.eq(User::getDeptId, 2)
.nested(qw -> qw.gt(User::getAge, 20).or().isNotNull(User::getEmail))
.orderByDesc(User::getCreateTime);
List<User> users = userMapper.selectList(wrapper);
三、LambdaQueryWrapper与QueryWrapper对比
1. 语法对比
特性 | LambdaQueryWrapper | QueryWrapper |
---|---|---|
字段引用方式 | 方法引用 (User::getName) | 字符串 ("name") |
编译时检查 | ✅ 字段名错误直接编译失败 | ❌ 运行时才能发现错误 |
IDE支持 | ✅ 自动补全、方法跳转 | ❌ 纯字符串无提示 |
重构安全性 | ✅ 字段重命名自动更新 | ❌ 需手动修改字符串 |
复杂条件可读性 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
2. 性能对比
- 编译阶段:
LambdaQueryWrapper
有轻微反射开销(仅首次) - 运行时:两者生成的SQL完全相同,性能无差异
- 内存占用:基本一致
3. 适用场景对比
场景 | LambdaQueryWrapper | QueryWrapper |
---|---|---|
固定字段条件查询 | ✅ 推荐 | ⚠️ 可用 |
动态字段名查询 | ❌ 不适用 | ✅ 必须 |
多表复杂关联查询 | ⚠️ 部分支持 | ✅ 更灵活 |
需要高可读性的项目 | ✅ 首选 | ❌ 不推荐 |
维护历史遗留代码 | ❌ 可能不兼容 | ✅ 兼容 |
四、最佳实践建议
1. 优先使用LambdaQueryWrapper的情况
// 1. 简单条件查询
lambdaWrapper.eq(User::getStatus, 1);
// 2. 多条件组合
lambdaWrapper.gt(User::getScore, 90)
.le(User::getAge, 30);
// 3. 嵌套查询
lambdaWrapper.nested(qw ->
qw.eq(User::getType, 1).or().eq(User::getType, 2)
);
2. 必须使用QueryWrapper的情况
// 1. 动态字段名查询
String fieldName = isNewVersion ? "new_name" : "name";
queryWrapper.eq(fieldName, "张三");
// 2. 数据库函数使用
queryWrapper.apply("date_format(create_time,'%Y-%m')={0}", "2023-01");
// 3. 复杂SQL片段
queryWrapper.exists("select 1 from dept where dept.id=user.dept_id and dept.status=1");
3. 混合使用技巧
// 在LambdaQueryWrapper中嵌入QueryWrapper
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.and(qw -> {
QueryWrapper<User> query = new QueryWrapper<>();
query.apply("(score > 80 or honor_count > 5)");
return qw.add(query);
});
五、高级用法示例
1. 条件优先级控制
// 使用括号明确优先级
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeptId, 2)
.and(qw -> qw.gt(User::getAge, 25).or().eq(User::getTitle, "经理"));
// 生成SQL: dept_id = 2 AND (age > 25 OR title = '经理')
2. 链式调用优化
// 使用静态导入简化代码
import static com.baomidou.mybatisplus.core.toolkit.Wrappers.*;
List<User> users = userMapper.selectList(
lambdaQuery(User.class)
.eq(User::getName, "张三")
.gt(User::getAge, 18)
.list()
);
3. 批量查询构建
// 构建批量更新条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.in(User::getId, Arrays.asList(1, 2, 3))
.set(User::getStatus, 1)
.set(User::getUpdateTime, LocalDateTime.now());
userMapper.update(null, wrapper);
六、总结与选择建议
- 新项目开发:优先使用
LambdaQueryWrapper
,享受类型安全和代码可读性优势 - 动态字段场景:必须使用
QueryWrapper
处理运行时确定的字段名 - 复杂SQL:
QueryWrapper
对原生SQL支持更好 - 团队协作:统一代码风格,避免混用造成混乱
终极建议:在保证功能实现的前提下,尽可能使用LambdaQueryWrapper
,只在必要场景下使用QueryWrapper
。两者配合使用可以发挥MyBatis-Plus最大的查询能力。
通过合理选择查询构造器,可以显著提升代码质量、开发效率和系统可维护性。希望本文能帮助您在项目中做出更明智的技术选型。