MyBatis-Plus 中 and() 与 or() 的嵌套组合:构建复杂查询条件的实战解析

MyBatis-Plus 中 and() 与 or() 的嵌套组合:构建复杂查询条件的实战解析 1. 初识MyBatis-Plus的条件构造器第一次接触MyBatis-Plus的QueryWrapper时我就被它的简洁语法惊艳到了。相比传统的MyBatis需要手写XML SQL语句QueryWrapper通过链式调用就能构建复杂的查询条件。特别是and()和or()这两个方法简直就是处理多条件查询的神器。记得我刚接手一个后台管理系统时需要实现用户的多条件筛选功能。比如要查询年龄大于25岁且姓名包含张或手机号以138开头的用户。如果用传统方式这个SQL写起来会很复杂而QueryWrapper只需要几行代码就能搞定QueryWrapperUser wrapper new QueryWrapper(); wrapper.gt(age, 25) .and(qw - qw.like(name, 张).or().like(phone, 138%));这段代码生成的SQL正是我们想要的WHERE age 25 AND (name LIKE %张% OR phone LIKE 138%)。这种Lambda表达式的写法不仅直观还能自动处理括号的嵌套关系大大减少了出错的可能性。2. 理解and()和or()的基本用法2.1 and()方法的本质and()在QueryWrapper中表示逻辑与的关系。它有两种使用方式隐式调用直接链式调用条件方法时默认就是and关系wrapper.eq(status, 1).ge(create_time, 2023-01-01); // 等同于 WHERE status 1 AND create_time 2023-01-01显式调用通过and()方法包裹条件组wrapper.eq(dept_id, 10) .and(qw - qw.gt(salary, 5000).lt(salary, 10000)); // 等同于 WHERE dept_id 10 AND (salary 5000 AND salary 10000)实际项目中我发现显式使用and()的主要场景是需要对一组条件整体进行AND运算时特别是这组条件本身又包含OR关系的时候。2.2 or()方法的妙用or()表示逻辑或的关系同样有两种使用方式简单条件OR连接wrapper.eq(type, 1).or().eq(type, 2); // WHERE type 1 OR type 2复杂条件OR组合wrapper.eq(status, 1) .or(qw - qw.eq(status, 2).ge(create_time, 2023-01-01)); // WHERE status 1 OR (status 2 AND create_time 2023-01-01)在商品筛选功能中我经常用or()来实现满足A条件或者同时满足B和C条件这类需求。比如查询特价商品或新品且库存充足的商品列表。3. 嵌套组合的实战技巧3.1 实现A AND (B OR C)结构这是最常见的嵌套需求之一。假设我们要查询部门ID为10且职称为高级或者入职满3年的员工QueryWrapperEmployee wrapper new QueryWrapper(); wrapper.eq(dept_id, 10) .and(qw - qw.eq(title, 高级).or().ge(hire_years, 3));这里的关键点是先用eq()设置dept_id条件然后用and()包裹一个Lambda表达式在Lambda内部使用or()连接两个条件生成的SQL是WHERE dept_id 10 AND (title 高级 OR hire_years 3)3.2 实现A OR (B AND C)结构另一种常见场景是满足A条件或者同时满足B和C条件。比如查询状态为待审核或者创建时间在今天且优先级高的工单QueryWrapperOrder wrapper new QueryWrapper(); wrapper.eq(status, 待审核) .or(qw - qw.ge(create_time, LocalDate.now()) .eq(priority, 高));这个例子中先用eq()设置status条件然后用or()包裹Lambda表达式Lambda内部用ge()和eq()实现AND关系生成的SQL是WHERE status 待审核 OR (create_time 2023-07-20 AND priority 高)3.3 多层嵌套的复杂场景在一些特别复杂的查询中可能需要三层甚至更多层的嵌套。比如查询部门为技术部且职称为高级或工作满5年或部门为市场部且KPI达标的员工QueryWrapperEmployee wrapper new QueryWrapper(); wrapper.and(qw1 - qw1.eq(dept, 技术部) .and(qw2 - qw2.eq(title, 高级) .or().ge(work_years, 5))) .or(qw - qw.eq(dept, 市场部) .eq(kpi, 达标));这种多层嵌套的写法虽然复杂但只要遵循每个括号对应一个Lambda表达式的原则就能清晰地构建出想要的查询结构。4. 常见问题与解决方案4.1 括号位置错误的排查刚开始使用嵌套查询时我最常遇到的问题就是生成的SQL括号位置不对。比如想要A AND (B OR C)结果生成了(A AND B) OR C。这种情况通常是因为Lambda表达式的作用范围没搞清楚。错误示例// 错误的写法生成的SQL是 (dept_id 10 AND title 高级) OR hire_years 3 wrapper.eq(dept_id, 10) .eq(title, 高级) .or().ge(hire_years, 3);正确写法// 正确的写法生成的SQL是 dept_id 10 AND (title 高级 OR hire_years 3) wrapper.eq(dept_id, 10) .and(qw - qw.eq(title, 高级).or().ge(hire_years, 3));4.2 条件优先级混淆另一个常见问题是逻辑运算符的优先级混淆。SQL中AND的优先级高于OR所以A AND B OR C实际上等同于(A AND B) OR C。如果不加括号明确优先级可能会得到不符合预期的查询结果。解决方案明确使用括号来指定优先级复杂的条件组合尽量拆分成多个Lambda表达式使用QueryWrapper的nested()方法进行更精细的控制4.3 动态条件构建技巧在实际项目中查询条件往往是动态的。比如用户可能只填写了部分筛选条件。这时候就需要动态构建QueryWrapperQueryWrapperUser wrapper new QueryWrapper(); if (StringUtils.isNotBlank(name)) { wrapper.like(name, name); } if (minAge ! null) { wrapper.ge(age, minAge); } if (maxAge ! null) { wrapper.le(age, maxAge); } if (hobbies ! null !hobbies.isEmpty()) { wrapper.and(qw - { for (String hobby : hobbies) { qw.or().like(hobbies, hobby); } }); }这种写法可以灵活应对各种条件组合而且生成的SQL都是正确嵌套的。5. 性能优化建议5.1 避免过度嵌套虽然嵌套查询很强大但过度嵌套会影响SQL性能。特别是在处理大数据表时建议嵌套层级不要超过3层复杂的查询考虑拆分成多个简单查询必要时使用JOIN代替嵌套条件5.2 索引友好型写法为了让查询能够利用索引在构建条件时要注意等值条件()放在OR条件的前面范围查询(,)尽量放在最后避免在索引列上使用NOT、!等否定操作例如// 好的写法能够利用dept_id和status的索引 wrapper.eq(dept_id, 10) .and(qw - qw.eq(status, 1).or().eq(status, 2)); // 不好的写法无法利用索引 wrapper.eq(dept_id, 10) .and(qw - qw.ne(status, 0).or().eq(status, 3));5.3 条件顺序优化条件的顺序也会影响查询性能。一般原则是选择性高的条件放在前面能够过滤掉大量数据的条件优先执行计算成本高的条件尽量靠后比如查询活跃用户// 优化后的写法先过滤掉非活跃用户 wrapper.eq(is_active, 1) .and(qw - qw.ge(last_login, 2023-01-01) .or().gt(login_count, 100));6. 实际项目中的应用案例6.1 电商商品筛选系统在一个电商后台我们需要实现这样的商品筛选分类为电子产品价格在1000-5000之间并且是新品或评分大于4.5用QueryWrapper实现如下QueryWrapperProduct wrapper new QueryWrapper(); wrapper.eq(category, 电子产品) .between(price, 1000, 5000) .and(qw - qw.eq(is_new, true).or().gt(rating, 4.5));这个查询用到了and()和or()的嵌套组合生成的SQL既清晰又高效。6.2 人力资源管理系统在员工查询功能中需要支持这样的条件部门为研发部或测试部并且职称为高级工程师或工作年限大于3年实现代码QueryWrapperEmployee wrapper new QueryWrapper(); wrapper.and(qw - qw.eq(dept, 研发部).or().eq(dept, 测试部)) .and(qw - qw.eq(title, 高级工程师).or().gt(work_years, 3));这种写法既保持了代码的可读性又准确表达了复杂的业务逻辑。6.3 内容管理系统在文章管理后台常见的查询需求如状态为已发布并且创建时间在最近7天或阅读量大于1000并且标签包含Java或标签包含SpringQueryWrapper实现QueryWrapperArticle wrapper new QueryWrapper(); wrapper.eq(status, 已发布) .and(qw - qw.ge(create_time, LocalDate.now().minusDays(7)) .or().gt(view_count, 1000)) .and(qw - { qw.like(tags, Java).or().like(tags, Spring); });这个例子展示了多层次的嵌套组合每个括号都对应着业务逻辑中的一个明确分组。