第一次接触MybatisPlus的模糊查询功能时,我就像发现新大陆一样兴奋。记得当时接手一个后台管理系统,需要实现根据用户输入的关键词同时搜索用户名、手机号和地址多个字段。传统做法要写一堆if-else判断,而MybatisPlus的QueryWrapper让这一切变得异常简单。
**核心方法.like()**的使用就像搭积木一样直观。比如要查询用户表中姓名包含"张"或地址包含"北京"的记录,代码可以这样写:
java复制QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("username", "张")
.or()
.like("address", "北京");
List<User> users = userMapper.selectList(wrapper);
这段代码生成的SQL相当于:
sql复制SELECT * FROM user WHERE username LIKE '%张%' OR address LIKE '%北京%'
实际项目中我发现几个容易踩的坑:
LIKE '%value%'对于需要精确控制匹配位置的情况,MybatisPlus还提供了:
.likeLeft() 相当于 LIKE '%value'.likeRight() 相当于 LIKE 'value%'我在电商项目里就用过likeRight实现商品编号前缀搜索,比全模糊查询效率高很多。
当查询条件变得复杂时,正确的条件组合方式直接影响查询结果。有次我遇到个需求:要查询所有VIP用户,或者普通用户中消费超过1000元且地址在北京的。刚开始写的查询条件完全跑偏,后来才搞明白and和or的优先级问题。
条件优先级的处理有个实用技巧 - 使用lambda表达式:
java复制wrapper.and(qw -> qw.eq("user_type", "VIP")
.or()
.eq("user_type", "NORMAL")
.gt("total_cost", 1000)
.like("address", "北京"));
这个例子生成的SQL是:
sql复制WHERE (user_type = 'VIP' OR (user_type = 'NORMAL' AND total_cost > 1000 AND address LIKE '%北京%'))
对比下两种写法的区别:
| 写法 | SQL示例 | 适用场景 |
|---|---|---|
| 链式调用 | field1=A OR field2=B AND field3=C | 简单条件组合 |
| Lambda嵌套 | (field1=A OR (field2=B AND field3=C)) | 复杂条件优先级控制 |
实际开发中我总结的经验:
有个性能优化的小技巧:对于确定要使用索引的字段,应该把它的条件放在前面。比如用户ID的条件应该优先于用户名的模糊匹配。
当遇到前端传过来多个关键词需要同一字段模糊查询时,问题就变得更有挑战性了。比如用户可以在搜索框输入"北京 上海",要求同时匹配这两个关键词。
错误做法是直接循环拼接or条件:
java复制// 这是错误示范!
String[] keywords = searchStr.split(" ");
QueryWrapper<User> wrapper = new QueryWrapper<>();
for (String keyword : keywords) {
wrapper.or().like("address", keyword);
}
这样会产生N个OR条件,当关键词多时性能很差。我在日志系统里就遇到过这样的问题,一个搜索包含5个关键词时查询耗时超过2秒。
推荐方案是使用条件构造器动态拼接:
java复制String[] keywords = searchStr.split(" ");
QueryWrapper<User> wrapper = new QueryWrapper<>();
if (keywords.length > 0) {
wrapper.and(qw -> {
for (String keyword : keywords) {
qw.or().like("address", keyword);
}
});
}
对于需要同时匹配多个关键词的场景(相当于AND关系),可以这样写:
java复制wrapper.and(qw -> {
for (String keyword : keywords) {
qw.like("address", keyword);
}
});
实际项目中我还遇到过需要根据不同参数动态构建查询的情况。比如:
java复制public List<User> searchUsers(UserQuery query) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
if (StringUtils.isNotBlank(query.getName())) {
wrapper.like("username", query.getName());
}
if (query.getMinAge() != null) {
wrapper.ge("age", query.getMinAge());
}
// 其他条件...
return userMapper.selectList(wrapper);
}
这种动态查询构造特别适合后台管理系统的筛选功能。
模糊查询最容易出现性能问题,特别是在数据量大的表中。有次我们用户表超过100万数据时,一个简单的模糊查询竟然要3秒多,页面直接超时了。
索引优化是首先要考虑的。虽然普通B树索引对LIKE '%xxx%'无效,但可以:
LIKE 'xxx%'使用likeRight()可以利用索引查询优化的几个实用技巧:
java复制wrapper.select("id", "username", "phone");
java复制Page<User> page = new Page<>(1, 20);
userMapper.selectPage(page, wrapper);
缓存策略也很重要。对于相对静态的数据,可以:
在最近的项目中,我通过组合这些优化手段,将一个原本需要5秒的模糊查询优化到了200毫秒内。关键是把likeRight、字段限制、分页和缓存都用上了。
当业务需求变得更复杂时,基本的like查询可能就不够用了。比如需要实现拼音搜索、简繁转换或者更高级的文本匹配。
拼音搜索可以通过额外存储拼音字段实现:
java复制// 存储时同时保存拼音
user.setUsernamePinyin(PinyinUtils.toPinyin(username));
// 查询时
wrapper.like("username_pinyin", PinyinUtils.toPinyin(keyword));
简繁转换也是类似思路,存储转换后的文本并查询。
对于高级文本搜索需求,可以考虑:
在最近的一个知识库项目中,我们就用MybatisPlus的like查询做第一层过滤,然后用Elasticsearch做精确匹配和相关性排序,效果非常好。
还有一种特殊场景是多字段联合模糊匹配,比如要查询"姓名或电话包含123"的用户:
java复制wrapper.and(qw -> qw.like("username", keyword)
.or()
.like("phone", keyword));
这种查询要特别注意索引的使用,避免全表扫描。