MyBatis 模糊查询终极教程:安全高效的搜索实现
一、模糊查询的核心概念
什么是模糊查询?
模糊查询是数据库搜索中允许部分匹配的技术,使用 SQL 的 LIKE
关键字配合通配符实现:
%
匹配任意数量字符_
匹配单个字符
示例场景
用户输入部分名称搜索食材:
输入"苹果" → 匹配"红富士苹果"、"苹果汁"等
输入"果" → 匹配所有包含"果"字的食材
二、安全模糊查询的核心实现
安全模糊查询模板
<select id="searchFoods" resultType="cn.cjxy.domain.FoodInfo">SELECT id, name, type, price, buyer, buy_timeFROM tb_foods<where><if test="type != null and type != ''">type LIKE CONCAT('%', #{type}, '%')</if><if test="name != null and name != ''">AND name LIKE CONCAT('%', #{name}, '%')</if></where>ORDER BY buy_time DESC
</select>
安全三要素解析
- **预编译占位符 **
#{}
- 不是字符串拼接,而是将参数作为整体传给数据库
- 用户输入始终被当作数据值处理,而非SQL代码
- 即使输入包含SQL关键字(
SELECT
、DELETE
等)也不会影响结构
- CONCAT函数的作用
CONCAT('%', #{input}, '%')
- 构建完整的模糊匹配模式
- 保证通配符位置固定(两边都有%)
- 避免用户控制模糊匹配的范围
- 动态条件判断
<if test="type != null and type != ''">
- 只有当参数非空时添加条件
- 提高查询效率
- 避免空条件污染SQL
三、为什么这种实现最安全?
SQL注入攻击场景模拟
假设恶意用户输入:' OR '1'='1
- 安全实现的效果
-- 实际执行的SQL
WHERE name LIKE CONCAT('%', '\' OR \'1\'=\'1', '%')
- 搜索名称包含 `' OR '1'='1` 的记录
- 不会返回所有数据
- 不安全的写法对比
<!-- 危险!直接字符串拼接 -->
WHERE name LIKE '%${input}%'
-- 执行的SQL (危险!)
WHERE name LIKE '%' OR '1'='1%'
-- 等同于
WHERE 1=1 -- 返回所有数据!
四、高级模糊查询技巧
1. 精确位置控制
<!-- 前缀匹配 (apple%) -->
LIKE CONCAT(#{input}, '%')<!-- 后缀匹配 (%apple) -->
LIKE CONCAT('%', #{input})<!-- 固定位置匹配 (a_pl_) -->
LIKE CONCAT('a_', #{input}, '_e%')
2. 特殊字符处理
<if test="name != null">name LIKE CONCAT('%', REPLACE(#{name}, '_', '\\_'), '%') ESCAPE '\'
</if>
- 转义
_
和%
字符 - 使用
ESCAPE
关键字定义转义符
3. 大小写不敏感查询
LOWER(name) LIKE CONCAT('%', LOWER(#{name}), '%')
五、性能优化指南
1. 索引使用策略
查询类型 | 能否使用索引 | 优化建议 |
---|---|---|
LIKE 'value%' | ✅ 是 | 最适合索引 |
LIKE '%value' | ❌ 否 | 考虑反向索引 |
LIKE '%value%' | ❌ 否 | 限制结果数量 |
2. 覆盖索引优化
ALTER TABLE tb_foods ADD INDEX idx_name_type (name, type);
3. 分页控制结果集
<select id="searchFoods" resultType="...">...LIMIT #{offset}, #{pageSize}
</select>
六、最佳实践总结
- **始终使用
#{}
+ **CONCAT()
- 绝对安全的标准做法
- 全数据库兼容
- 动态条件选择
- 使用
<if test="...">
仅添加有效条件 - 避免空条件降低性能
- 使用
- 特殊场景处理
- 转义特殊字符(
%, _
) - 使用
LOWER()
忽略大小写 - 对于固定前缀使用
LIKE 'prefix%'
- 转义特殊字符(
- 性能考量
- 为常用搜索字段建立索引
- 避免全表扫描的模糊查询
- 使用分页限制结果数量
- 参数预处理
// 在Service层处理参数
if (input != null) {// 去除空格input = input.trim();// 限制长度input = input.length() > 50 ? input.substring(0, 50) : input;
}
七、完整实现流程图
这种模糊查询实现方式提供了企业级的安全保障,同时保持了查询的灵活性和高性能。它在保持易用性的同时,从根本上杜绝了SQL注入风险,是开发搜索功能时的首选方案。