一、核心概念
- 嵌套查询
- 子查询(IN/NOT IN/EXISTS)
- 关联表嵌套查询
- 函数调用
- SQL 函数(CONCAT/COUNT/SUM)
- 自定义函数
- 条件构造器
QueryWrapper
/LambdaQueryWrapper
- 链式调用与条件组合
二、详细操作步骤
1. 嵌套查询实现
方式一:使用 inSql
/notInSql
// 子查询:查询部门ID在财务部或技术部的员工
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.inSql("dept_id",
"SELECT id FROM department WHERE name IN ('财务部','技术部')"
);
// 等效SQL:
// SELECT * FROM employee
// WHERE dept_id IN (SELECT id FROM department WHERE name IN ('财务部','技术部'))
方式二:使用 exists
/notExists
// 存在性查询:有订单的员工
wrapper.exists("SELECT 1 FROM orders o WHERE o.employee_id = employee.id");
// 等效SQL:
// SELECT * FROM employee e
// WHERE EXISTS (SELECT 1 FROM orders o WHERE o.employee_id = e.id)
方式三:Lambda 嵌套查询
LambdaQueryWrapper<Employee> lambdaWrapper = Wrappers.lambdaQuery();
lambdaWrapper.inSql(Employee::getDeptId,
"SELECT id FROM department WHERE status = 1"
);
2. 函数调用实现
方式一:apply()
方法
// 调用日期函数
wrapper.apply("DATE_FORMAT(create_time,'%Y-%m-%d') = '2023-05-01'");
// 字符串拼接
wrapper.apply("CONCAT(first_name, last_name) LIKE {0}", "%张伟%");
方式二:func()
方法(Lambda)
lambdaWrapper.func(i -> {
if (useFullName) {
i.apply("CONCAT(first_name, last_name) LIKE {0}", "%张伟%");
} else {
i.like(Employee::getFirstName, "张");
}
});
方式三:聚合函数
// 分组统计
QueryWrapper<Department> groupWrapper = new QueryWrapper<>();
groupWrapper.select("dept_id", "COUNT(*) as emp_count")
.groupBy("dept_id")
.having("emp_count > 10");
3. 条件构造器高级用法
复杂条件组合
wrapper.nested(w -> w.like("name", "张").or().like("name", "王"))
.and(w -> w.gt("age", 25).lt("age", 40))
.orderByDesc("salary");
// 等效SQL:
// WHERE (name LIKE '%张%' OR name LIKE '%王%')
// AND (age > 25 AND age < 40)
// ORDER BY salary DESC
动态条件构建
public List<Employee> dynamicQuery(String name, Integer minAge, Integer maxAge) {
return lambdaWrapper
.like(StringUtils.isNotBlank(name), Employee::getName, name)
.ge(minAge != null, Employee::getAge, minAge)
.le(maxAge != null, Employee::getAge, maxAge)
.list();
}
联表查询条件
// 多表关联条件
wrapper.eq("department.status", 1)
.like("department.name", "技术")
.inSql("employee.id",
"SELECT employee_id FROM project WHERE status = 1"
);
三、常见错误与解决
嵌套查询结果集过大
- 错误:
IN
子查询返回超过1000条记录 - 解决:改用
JOIN
或分批次查询
- 错误:
SQL注入风险
- 错误:
apply("CONCAT(name, '"+input+"')")
- 解决:使用预编译占位符
apply("CONCAT(name, {0})", input)
- 错误:
函数兼容性问题
- 错误:
DATE_FORMAT
在Oracle中不兼容 - 解决:使用数据库方言或MyBatis-Plus的
DbType
配置
- 错误:
条件顺序错误
- 错误:
or()
连接符位置不当导致逻辑错误 - 解决:使用
nested()
明确条件分组
- 错误:
四、注意事项
性能敏感操作
- 避免在循环中构建查询条件(每次
new QueryWrapper()
) - 大数据量
IN
查询改用JOIN
- 避免在循环中构建查询条件(每次
跨数据库兼容
- 函数调用使用
AbstractSqlParser
处理方言差异 - 分页查询避免
SELECT *
- 函数调用使用
空值处理
- 使用
condition
参数自动跳过空条件:wrapper.like(StringUtils.isNotBlank(name), "name", name)
- 使用
XML 与注解优先级
- 条件构造器优先级高于 XML 中的
<where>
- 条件构造器优先级高于 XML 中的
五、使用技巧
链式条件中断
wrapper.eq(...) .or() // 注意:此后条件会与前面所有条件OR .gt(...);
条件复用
Consumer<QueryWrapper<Employee>> commonCondition = w -> w.eq("status", 1).gt("salary", 10000); wrapper.nested(commonCondition).like("name", "张");
动态表名+条件构造器
wrapper.eq("tenant_id", TenantContext.getId()) .apply("${tableName}", getDynamicTable());
自定义SQL片段
@Select("SELECT * FROM employee ${ew.customSqlSegment}") List<Employee> selectByWrapper(@Param(Constants.WRAPPER) Wrapper wrapper);
六、最佳实践与性能优化
索引优化策略
- 条件顺序:高筛选度字段在前
- 避免函数包裹索引字段:
WHERE YEAR(create_time)=2023
→WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'
分页性能优化
// 优化COUNT SQL Page<Employee> page = new Page<>(1, 10).setOptimizeCountSql(false); page.setSearchCount(false); // 不执行COUNT查询
批处理嵌套查询
// 分批处理IN查询 (1000条/批) List<Long> ids = largeIdList; List<List<Long>> partitions = Lists.partition(ids, 1000); partitions.forEach(batch -> wrapper.or().in("id", batch) );
查询结果流式处理
try (Cursor<Employee> cursor = mapper.selectCursor(wrapper)) { cursor.forEach(entity -> process(entity)); }
二级缓存整合
@CacheNamespace(implementation = MybatisRedisCache.class) public interface EmployeeMapper extends BaseMapper<Employee> {}
七、复杂场景综合示例
场景:查询技术部薪资TOP10的员工
LambdaQueryWrapper<Employee> wrapper = Wrappers.lambdaQuery();
wrapper.select(Employee::getId, Employee::getName, Employee::getSalary)
.inSql(Employee::getDeptId,
"SELECT id FROM department WHERE name = '技术部'")
.apply("salary > (SELECT AVG(salary) FROM employee)")
.orderByDesc(Employee::getSalary)
.last("LIMIT 10");
// 等效SQL:
// SELECT id, name, salary FROM employee
// WHERE dept_id IN (SELECT id FROM department WHERE name = '技术部')
// AND salary > (SELECT AVG(salary) FROM employee)
// ORDER BY salary DESC
// LIMIT 10
八、调试与监控
SQL输出配置
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
性能分析插件
@Bean public PerformanceInterceptor performanceInterceptor() { PerformanceInterceptor interceptor = new PerformanceInterceptor(); interceptor.setMaxTime(1000); // SQL超时阈值(ms) interceptor.setFormat(true); // 格式化SQL return interceptor; }
Explain分析
wrapper.last("EXPLAIN FORMAT=JSON"); String explainResult = mapper.selectOne(wrapper).toString();
通过掌握这些高级技巧,可应对:
- 多层级嵌套查询
- 动态函数调用
- 复杂条件组合
- 大数据量性能优化
- 跨数据库兼容方案
关键要点:
- 优先使用 Lambda 表达式避免字段硬编码
- 嵌套查询用
nested()
明确条件分组 - 函数调用用
apply()
保证预编译安全 - 大数据场景采用分批查询+流式处理