1. QueryWrapper基础入门刚接触MyBatis-Plus时最让我眼前一亮的特性就是QueryWrapper。这个条件构造器彻底改变了我们编写SQL条件的方式就像搭积木一样简单。想象一下以前我们要写一大堆XML或者注解SQL现在只需要几行Java代码就能搞定复杂查询。QueryWrapper的核心原理其实很简单 - 它通过链式调用方法帮我们动态拼接WHERE条件。比如要查询年龄大于20岁的用户以前得写WHERE age 20现在只需要wrapper.gt(age, 20)。我特别喜欢它的lambda表达式支持用起来特别顺手// 传统方式 QueryWrapperUser wrapper new QueryWrapper(); wrapper.gt(age, 20); // Lambda方式推荐 LambdaQueryWrapperUser lambdaWrapper new LambdaQueryWrapper(); lambdaWrapper.gt(User::getAge, 20);这两种方式生成的SQL完全一样但Lambda写法更安全因为它是编译时检查的。我刚开始用的时候经常把字段名写错用Lambda就再也不用担心这个问题了。2. 核心条件方法详解2.1 等值查询与范围查询最常用的eq方法相当于SQL中的比如查询名字叫张三的用户wrapper.eq(name, 张三); // 或者Lambda写法 lambdaWrapper.eq(User::getName, 张三);范围查询也很常用比如查询年龄在20到30之间的用户// 大于 wrapper.gt(age, 20); // 大于等于 wrapper.ge(age, 20); // 小于 wrapper.lt(age, 30); // 小于等于 wrapper.le(age, 30); // 区间查询 wrapper.between(age, 20, 30);实际项目中我经常用between来做日期范围查询比如查询最近一周的订单LocalDate now LocalDate.now(); wrapper.between(create_time, now.minusDays(7), now);2.2 模糊查询与空值处理模糊查询在搜索功能中特别有用like方法相当于SQL的LIKE// %值%包含 wrapper.like(name, 张); // %值左匹配 wrapper.likeLeft(name, 张); // 值%右匹配 wrapper.likeRight(name, 三);处理空值也很重要我遇到过不少NPE问题就是因为没处理好null// 字段为null wrapper.isNull(description); // 字段不为null wrapper.isNotNull(description);2.3 集合操作与子查询in查询在批量操作时特别高效// 普通in查询 wrapper.in(id, 1, 2, 3); // 子查询 wrapper.inSql(dept_id, select id from dept where status1);有个小技巧当in的集合很大时比如超过1000个Oracle会有性能问题这时可以用分批查询。3. 链式调用与条件组合3.1 AND/OR逻辑组合QueryWrapper最强大的地方就是可以像拼积木一样组合条件// AND连接默认 wrapper.eq(dept_id, 1) .gt(salary, 5000); // OR连接 wrapper.eq(dept_id, 1) .or() .eq(dept_id, 2);实际项目中我经常用这种链式调用来构建动态查询条件。比如后台管理系统的筛选功能if (StringUtils.isNotBlank(name)) { wrapper.like(name, name); } if (minSalary ! null) { wrapper.ge(salary, minSalary); }3.2 嵌套条件与动态SQL复杂查询时可以用嵌套条件wrapper.nested(i - i.eq(type, 1).or().eq(type, 2)) .gt(create_time, 2023-01-01);这相当于SQL中的括号优先级控制。我处理过一个权限系统需要这样的复杂条件WHERE (role_id1 OR role_id2) AND department_id3用QueryWrapper可以这样写wrapper.nested(i - i.eq(role_id, 1).or().eq(role_id, 2)) .eq(department_id, 3);4. 分页插件配置实战4.1 Spring Boot环境配置MyBatis-Plus的分页功能需要先配置插件。在Spring Boot中很简单Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }注意这里用的是最新版的配置方式老版的PaginationInterceptor已经过时了。我升级时就踩过这个坑新版本还支持多租户、乐观锁等插件。4.2 基本分页查询配置好插件后分页查询就很简单了// 第一页每页10条 PageUser page new Page(1, 10); // 添加查询条件 LambdaQueryWrapperUser wrapper new LambdaQueryWrapper(); wrapper.gt(User::getAge, 20); // 执行查询 PageUser result userMapper.selectPage(page, wrapper); // 获取分页信息 long total result.getTotal(); // 总记录数 long pages result.getPages(); // 总页数 ListUser records result.getRecords(); // 当前页数据4.3 自定义分页查询有时我们需要在分页时查询关联表可以这样写Select(select u.*, d.name as dept_name from user u left join dept d on u.dept_idd.id ${ew.customSqlSegment}) PageUserVO selectUserPage(PageUserVO page, Param(Constants.WRAPPER) WrapperUserVO wrapper);注意这里的${ew.customSqlSegment}会自动注入QueryWrapper生成的WHERE条件。5. 高级技巧与最佳实践5.1 性能优化建议索引命中确保QueryWrapper中的条件字段都有索引避免全表扫描不要对未索引的字段做、!、not in等操作分批处理大数据量时使用limit分批查询SQL监控开启MyBatis-Plus的SQL日志检查生成的SQL5.2 常见问题排查问题1分页查询返回total不正确解决检查是否配置了分页插件数据库类型是否正确问题2LambdaQueryWrapper编译报错解决确认实体类有对应的getter方法问题3条件拼接错误解决打印最终SQL检查可以用wrapper.getCustomSqlSegment()5.3 实际项目经验在我们电商项目中商品搜索用QueryWrapper实现了非常复杂的条件组合LambdaQueryWrapperProduct wrapper new LambdaQueryWrapper(); wrapper.eq(Product::getStatus, 1) .between(Product::getPrice, minPrice, maxPrice) .in(Product::getCategoryId, categoryIds) .and(q - q.like(Product::getName, keyword) .or() .like(Product::getDescription, keyword)) .orderByDesc(Product::getSales);配合前端传参可以灵活构建各种查询条件。分页查询时我们还加了缓存机制避免重复计算total。