在使用 MyBatis-Plus 进行企业级 Java 开发时,复杂查询条件的构建往往是提升系统灵活性与可维护性的关键环节。相比传统 XML 手写 SQL,MyBatis-Plus 提供的 Wrapper 体系能够通过链式调用方式动态拼装查询逻辑,在多条件组合、嵌套逻辑、动态筛选等场景下显著降低开发成本。
在实际业务中,查询条件通常不是固定的,而是由前端参数动态决定,例如用户筛选、分页查询、权限过滤等。因此,掌握 MyBatis-Plus 构建复杂 SQL 查询条件的方法,是提升开发效率与代码质量的重要技能。
一、QueryWrapper 构建基础查询条件
QueryWrapper 是 MyBatis-Plus 中最常用的条件构造器,用于非 Lambda 方式的字段拼接。
常见基础用法如下:
JavaQueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("status", 1)
.like("username", "tom")
.ge("age", 18);
上述代码对应 SQL:
SQLWHERE status = 1 AND username LIKE '%tom%' AND age >= 18
在简单查询中,QueryWrapper 已经能够满足大部分需求,但在复杂业务场景中,推荐进一步使用 LambdaQueryWrapper 来避免字段硬编码问题。
二、LambdaQueryWrapper 提升类型安全性
LambdaQueryWrapper 通过方法引用方式引用实体字段,避免字符串字段带来的拼写错误问题。
JavaLambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1)
.like(User::getUsername, "tom")
.ge(User::getAge, 18);
这种方式在重构时具有更高安全性,是企业项目中的推荐写法。
三、动态条件构建(核心能力)
在实际开发中,查询条件通常需要根据参数是否为空进行动态拼接。
JavaLambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(status != null, User::getStatus, status)
.like(StringUtils.isNotBlank(username), User::getUsername, username)
.ge(minAge != null, User::getAge, minAge)
.le(maxAge != null, User::getAge, maxAge);
关键点在于第一个 boolean 参数,当条件为 false 时,该查询条件不会拼接到 SQL 中,从而实现动态 SQL 效果。
四、复杂逻辑 OR / AND 嵌套查询
复杂查询中最常见的场景是 AND 与 OR 的嵌套组合,例如:
JavaLambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1)
.and(w -> w.like(User::getUsername, "tom")
.or()
.like(User::getEmail, "tom"));
生成 SQL:
SQLWHERE status = 1 AND (username LIKE '%tom%' OR email LIKE '%tom%')
MyBatis-Plus 通过 and()、or() 方法支持子条件封装,从而实现逻辑分组。
五、多层嵌套复杂查询示例
在复杂业务系统中,经常会遇到多层嵌套条件,例如:
JavaLambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1)
.and(w1 -> w1
.ge(User::getAge, 18)
.and(w2 -> w2
.like(User::getUsername, "tom")
.or()
.like(User::getEmail, "tom")
)
);
对应 SQL:
SQLWHERE status = 1
AND (
age >= 18
AND (
username LIKE '%tom%'
OR email LIKE '%tom%'
)
)
这种结构在权限系统、复杂筛选条件中非常常见。
六、IN / NOT IN / BETWEEN 等高级条件
MyBatis-Plus 对常见 SQL 语法提供了完整封装。
IN 查询
Javawrapper.in(User::getId, idList);
NOT IN 查询
Javawrapper.notIn(User::getId, idList);
BETWEEN 查询
Javawrapper.between(User::getCreateTime, startTime, endTime);
这些方法在批量过滤、时间范围查询中非常高效。
七、排序与分页组合查询
复杂查询通常离不开分页与排序。
Javawrapper.orderByDesc(User::getCreateTime);
分页配合 MyBatis-Plus 分页插件:
JavaPage<User> page = new Page<>(current, size);
userMapper.selectPage(page, wrapper);
在高并发系统中,分页与条件构建的组合是性能优化的重要基础。
八、子查询与 EXISTS 场景处理
MyBatis-Plus 支持通过 apply 方法实现更复杂 SQL:
Javawrapper.apply("exists (select 1 from order o where o.user_id = user.id and o.status = 1)");
该方式适用于无法通过 Wrapper 表达的复杂业务逻辑,例如 EXISTS、复杂 JOIN 条件等。
九、常见优化实践
在构建复杂查询条件时,应注意以下优化原则:
-
避免过度嵌套,保持逻辑清晰
-
优先使用 LambdaQueryWrapper 提高可维护性
-
动态条件必须避免 null 值污染 SQL
-
大数据查询慎用 like 模糊匹配
-
复杂 SQL 优先拆分为方法或 Service 层封装
-
尽量减少 apply 原生 SQL 使用频率
十、复杂查询封装设计思路
在企业级项目中,建议将复杂查询条件封装为独立方法:
Javaprivate LambdaQueryWrapper<User> buildCondition(UserQueryDTO dto) {
return Wrappers.lambdaQuery(User.class)
.eq(dto.getStatus() != null, User::getStatus, dto.getStatus())
.like(StringUtils.isNotBlank(dto.getKeyword()), User::getUsername, dto.getKeyword());
}
这种方式可以显著提升代码复用性,并降低 Controller 层复杂度。
MyBatis-Plus 在复杂 SQL 查询构建方面提供了强大的链式 API 支持,使开发者能够在无需编写大量 XML 的情况下完成灵活多变的业务查询逻辑。合理使用 Wrapper、动态条件与嵌套逻辑,可以在保证代码可读性的同时提升系统扩展能力。
[MyBatis-Plus, Java持久层框架, SQL查询优化, 动态查询条件, LambdaQueryWrapper]