1. MyBatis-Plus模糊查询入门指南
第一次接触MyBatis-Plus的模糊查询功能时,我也被like、likeLeft、likeRight这几个方法搞得晕头转向。明明看起来都是做模糊查询,为什么还要分这么多种?直到在实际项目中踩了几个坑之后,才真正理解了它们的区别和应用场景。
简单来说,这三个方法都是用来构建SQL中的LIKE条件,但处理通配符的方式不同:
- like:在参数两侧都加%通配符,相当于包含查询
- likeLeft:只在参数左侧加%通配符,相当于后缀匹配
- likeRight:只在参数右侧加%通配符,相当于前缀匹配
举个例子,假设我们有个用户表要按姓名搜索:
- 用like("张三")会匹配"老张三"、"张三丰"、"我叫张三"
- 用likeLeft("张三")只匹配"我叫张三"这类以"张三"结尾的
- 用likeRight("张三")只匹配"张三丰"这类以"张三"开头的
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 三种方法的实战应用场景
2.1 like方法:全模糊查询
全模糊查询是最常用的场景,比如电商平台的商品搜索功能。用户输入关键词后,我们希望匹配商品名称、描述等字段中包含该关键词的所有记录。
java复制// 搜索商品名称包含"手机"的所有商品
LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
wrapper.like(Product::getName, "手机");
List<Product> products = productMapper.selectList(wrapper);
这里有个实际开发中的经验:当数据量较大时,全模糊查询可能会导致性能问题。我曾经遇到过用户输入单个字母"a"就查询超时的情况,最后通过添加最小长度限制和缓存机制解决了这个问题。
2.2 likeRight方法:前缀匹配查询
前缀匹配特别适合有固定编码规则的数据查询。比如我们公司的订单编号格式是"年份+月份+日期+序列号"(如20230815001),要查某个月份的所有订单:
java复制// 查询2023年8月的所有订单
LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
wrapper.likeRight(Order::getOrderNo, "202308");
List<Order> orders = orderMapper.selectList(wrapper);
注意likeRight的参数不要带通配符,方法会自动添加。我曾经犯过这样的错误:
java复制wrapper.likeRight(Order::getOrderNo, "202308%"); // 错误!会变成"202308%%"
2.3 likeLeft方法:后缀匹配查询
后缀匹配的使用场景相对少一些,但某些特定需求下非常有用。比如查找所有以特定后缀结尾的文件:
java复制// 查找所有jpg图片
LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
wrapper.likeLeft(File::getFileName, ".jpg");
List<File> images = fileMapper.selectList(wrapper);
在用户手机号模糊查询时,如果只想匹配最后几位,也可以用likeLeft:
java复制// 匹配手机尾号是"888"的用户
wrapper.likeLeft(User::getPhone, "888");
3. 源码解析与性能优化
3.1 方法实现原理
查看MyBatis-Plus的源码,这三个方法最终都会调用StringUtils.concatLike()方法:
java复制public static String concatLike(Object str, SqlLike type) {
switch (type) {
case LEFT: return "%" + str;
case RIGHT: return str + "%";
default: return "%" + str + "%";
}
}
从源码可以看出:
- likeLeft就是在参数前加%
- likeRight就是在参数后加%
- like则是两边都加%
3.2 索引与性能考虑
模糊查询对数据库性能影响很大,特别是全模糊查询(like)无法使用普通索引。根据我的实测经验:
- likeRight查询可以使用索引(如果字段有索引的话),因为它是前缀匹配
- likeLeft和like查询会导致全表扫描,大数据量时要谨慎使用
- 对于必须使用like的场景,可以考虑以下优化方案:
- 添加最小搜索长度限制(如至少3个字符)
- 使用全文索引替代LIKE查询
- 考虑使用Elasticsearch等专业搜索工具
4. 常见问题与解决方案
4.1 方法名容易混淆的问题
很多开发者(包括最初的我)会以为:
- likeLeft是左匹配(实际是右匹配)
- likeRight是右匹配(实际是左匹配)
记忆技巧:把方法名中的Left/Right理解为"在哪个方向加%",而不是匹配方向。比如:
- likeLeft:在左边(left)加% → 匹配右边
- likeRight:在右边(right)加% → 匹配左边
4.2 特殊字符转义问题
当搜索内容包含SQL通配符(%、_)时,需要进行转义处理。MyBatis-Plus 3.4.0之后提供了自动转义:
java复制wrapper.like(Product::getName, "100%纯棉") // 会自动转义为"100\%纯棉"
对于老版本,可以手动转义:
java复制String keyword = "100%纯棉".replace("%", "\\%");
wrapper.like(Product::getName, keyword);
4.3 空字符串处理
当参数为空字符串时,不同数据库表现不同。MySQL会匹配所有记录,而Oracle可能返回空结果。安全做法是添加判空:
java复制if(StringUtils.isNotBlank(keyword)){
wrapper.like(Product::getName, keyword);
}
5. 高级用法与组合技巧
5.1 多条件组合查询
实际项目中经常需要组合多个模糊查询条件。比如搜索商品时同时匹配名称和描述:
java复制LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(name), Product::getName, name)
.like(StringUtils.isNotBlank(desc), Product::getDescription, desc);
这种写法会智能判断:只有当name不为空时才会添加name条件,desc同理。
5.2 动态选择查询方式
根据业务需求动态选择查询方式。比如精确匹配优先,没有结果时再尝试模糊匹配:
java复制List<User> users = userMapper.selectList(
new LambdaQueryWrapper<User>().eq(User::getName, keyword));
if(users.isEmpty()){
users = userMapper.selectList(
new LambdaQueryWrapper<User>().like(User::getName, keyword));
}
5.3 自定义通配符位置
如果需要更灵活的通配符位置(比如中间模糊),可以使用apply方法:
java复制wrapper.apply("name LIKE {0}", "%" + middle + "%");
不过这种方式要注意SQL注入风险,确保middle参数是可信的。
