MyBatis-Plus QueryWrapper 终极指南:语法详解与避坑实践

MyBatis-Plus QueryWrapper 终极指南:语法详解与避坑实践

在MyBatis-Plus的日常开发中,QueryWrapper作为条件构造器的核心角色,掌握了它的使用技巧就等于掌握了高效数据库操作的钥匙。本文将全面解析QueryWrapper的所有语法细节,并附上实际开发中必须警惕的注意事项。


一、QueryWrapper 核心语法大全

1. 基础比较操作
// 等于
qw.eq("name", "John"); 

// 不等于
qw.ne("age", 30);  

// 大于
qw.gt("price", 100);  

// 大于等于
qw.ge("create_time", "2023-01-01");  

// 小于
qw.lt("stock", 50);  

// 小于等于
qw.le("discount", 0.8);
2. 范围查询
// BETWEEN
qw.between("age", 18, 30);  

// NOT BETWEEN
qw.notBetween("salary", 5000, 10000);  

// IN
qw.in("department_id", Arrays.asList(1, 3, 5));  

// NOT IN
qw.notIn("status", 0, 4);  

// IN (SQL子查询)
qw.inSql("role_id", "SELECT id FROM role WHERE level > 3");
3. 模糊查询
// 全模糊:%value%
qw.like("title", "紧急");  

// 左模糊:%value
qw.likeLeft("code", "A001");  

// 右模糊:value%
qw.likeRight("phone", "138");  

// NOT LIKE
qw.notLike("content", "test");
4. 空值判断
qw.isNull("description");  
qw.isNotNull("update_time");
5. 分组与排序
// 分组
qw.groupBy("department", "position");  

// 排序
qw.orderByAsc("create_time");  
qw.orderByDesc("salary");  
qw.orderBy(true, true, "id", "name"); // 多个字段排序
6. 复杂条件组合
// AND 连接
qw.eq("deleted", 0)
  .ge("create_time", startDate)
  .le("create_time", endDate);

// OR 嵌套
qw.and(wrapper -> wrapper
    .eq("type", 1)
    .or()
    .eq("status", 2)
);

// 动态条件
String keyword = getParam();
qw.like(StringUtils.isNotBlank(keyword), "title", keyword);
7. 自定义SQL片段
// 直接拼接SQL
qw.apply("DATE_FORMAT(create_time,'%Y-%m') = {0}", "2023-08");

// EXISTS 子查询
qw.exists("SELECT 1 FROM user_log WHERE user_id = user.id");

二、LambdaQueryWrapper 类型安全写法

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.eq(User::getName, "John")
   .ge(User::getAge, 18)
   .between(User::getCreateTime, start, end);

三、避坑指南(重要!)

1. SQL注入风险
// 危险!直接拼接字符串
qw.apply("category_id = " + categoryId);

// ✅ 正确写法(使用预编译)
qw.apply("category_id = {0}", categoryId);
2. 空值处理原则
// 错误:空值会导致 WHERE name=null
qw.eq(String name, request.getName());

// ✅ 正确:动态条件
qw.eq(StringUtils.isNotBlank(name), "name", name);
3. 索引失效场景
// 导致索引失效的写法
qw.likeLeft("mobile", "13800138"); 

// ✅ 建议:右模糊保持索引有效性
qw.likeRight("mobile", "13800138");
4. 嵌套条件优先级
// 错误:逻辑混乱
qw.eq("A",1).or().eq("B",2).eq("C",3); 

// ✅ 正确:明确括号范围
qw.and(wrap -> wrap.eq("A",1).or().eq("B",2))
  .eq("C",3);
5. 分页查询陷阱
// 错误:分页+排序缺失
Page<User> page = new Page<>(1,10);
userMapper.selectPage(page, qw);

// ✅ 必须:分页必须指定排序字段
qw.orderByDesc("id");
6. 字段类型匹配
// 错误:字符串比较数字字段
qw.eq("status", "1"); // 数据库status是int

// ✅ 正确:类型严格一致
qw.eq("status", 1);
7. 实体属性映射
// 错误:使用数据库不存在的字段
qw.eq("createTime", value); // 实际字段是create_time

// ✅ 方案1:开启下划线转换
// 在配置中设置:mybatis-plus.configuration.map-underscore-to-camel-case=true

// ✅ 方案2:使用Lambda表达式
qw.eq(User::getCreateTime, value);

四、性能优化建议

  1. 避免全表扫描:WHERE条件中至少包含一个索引字段
  2. 控制返回字段:避免select *
    qw.select("id", "name"); // 显式指定字段
    
  3. 批处理操作:对于大量数据更新使用updateBatch
  4. 逻辑删除冲突:使用qw.eq("del_flag",0)会与MP全局逻辑删除配置冲突

五、最佳实践总结

  1. 优先使用LambdaQueryWrapper:编译期检查字段名有效性
  2. 复杂查询拆解:超过5个条件的查询建议拆分子方法
  3. SQL控制台输出:开发环境开启mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
  4. 组合查询规范
    // 清晰的代码结构
    qw.select("id", "name", "age")
      .eq("deleted", 0)
      .in("role", Arrays.asList(2,3,5))
      .groupBy("department")
      .having("count(1) > 5")
      .orderByDesc("create_time");
    

最后的提醒:永远不要相信前端传递的查询参数,所有传入Wrapper的条件必须经过业务层校验!


通过本文的详细梳理,相信您已全面掌握QueryWrapper的使用精髓。在实际开发中结合具体场景灵活运用这些技巧,将极大提升数据操作效率和代码健壮性。如果有更多特殊场景需求,欢迎在评论区交流讨论!

扩展阅读推荐

版权声明:本文为优快云博主「技术老宅」原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值