简介:本文详细解析MyBatis中LIKE模糊查询的四种实现方式,结合SQL注入防护、性能优化及多场景应用案例,帮助开发者写出高效安全的模糊查询代码。
在MyBatis开发中,模糊查询是高频需求,但看似简单的LIKE操作却暗藏SQL注入风险和性能陷阱。本文将系统梳理LIKE在MyBatis中的四种实现方式,结合安全防护、性能优化及多场景应用,帮助开发者写出既安全又高效的模糊查询代码。
<!-- 错误示例:存在SQL注入风险 --><select id="findByNameUnsafe" resultType="User">SELECT * FROM user WHERE name LIKE '%${name}%'</select>
问题分析:使用${}直接拼接字符串会导致SQL注入,攻击者可通过输入' OR '1'='1等恶意字符串破坏查询逻辑。
正确做法:必须使用#{}预编译参数,通过MyBatis的参数绑定机制自动转义特殊字符。
<!-- 正确示例1:参数预编译 --><select id="findByNameSafe" resultType="User">SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')</select>
技术要点:
CONCAT()函数拼接通配符#{}预编译,有效防止SQL注入变体实现:
<!-- MySQL特有语法 --><select id="findByNameSafe2" resultType="User">SELECT * FROM user WHERE name LIKE '%' || #{name} || '%'</select>
<!-- 动态SQL示例:支持前后模糊、前模糊、后模糊 --><select id="findByPattern" resultType="User">SELECT * FROM userWHERE name<choose><when test="pattern == 'both'">LIKE CONCAT('%', #{keyword}, '%')</when><when test="pattern == 'prefix'">LIKE CONCAT(#{keyword}, '%')</when><when test="pattern == 'suffix'">LIKE CONCAT('%', #{keyword})</when><otherwise>LIKE CONCAT('%', #{keyword}, '%')</otherwise></choose></select>
应用场景:
// Mapper接口@Select("SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')")List<User> findByName(@Param("name") String name);
优势:
@Param注解明确参数绑定反模式:
-- 对列使用函数会导致索引失效SELECT * FROM user WHERE UPPER(name) LIKE '%JOHN%'
优化方案:
CREATE INDEX idx_user_name_upper ON user(UPPER(name));
ALTER TABLE user ADD COLUMN name_upper VARCHAR(100)GENERATED ALWAYS AS (UPPER(name)) STORED;CREATE INDEX idx_name_upper ON user(name_upper);
<!-- 结合分页的模糊查询 --><select id="findByPage" resultType="User">SELECT * FROM userWHERE name LIKE CONCAT('%', #{keyword}, '%')LIMIT #{offset}, #{pageSize}</select>
关键指标:
// 使用二级缓存示例@CacheNamespacepublic interface UserMapper {@Select("SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')")@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE)List<User> findByNameCached(@Param("name") String name);}
适用场景:
// 前端输入校验public class InputValidator {public static boolean isValidSearchKeyword(String keyword) {return keyword != null&& keyword.length() <= 50&& !keyword.matches(".*[;'\"]+.*");}}
校验规则:
// 自定义类型处理器public class SafeLikeParameterHandler implements TypeHandler<String> {@Overridepublic void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {if (parameter != null) {parameter = parameter.replace("%", "\\%").replace("_", "\\_");}ps.setString(i, "%" + parameter + "%");}}
<!-- 开启MyBatis日志 --><settings><setting name="logImpl" value="STDOUT_LOGGING"/></settings>
日志内容建议:
<!-- MySQL特有语法 --><select id="findByLikeMySQL" resultType="User">SELECT * FROM user WHERE name LIKE BINARY #{name} ESCAPE '\\'</select>
特性:
BINARY强制区分大小写ESCAPE指定转义字符
<!-- Oracle实现 --><select id="findByLikeOracle" resultType="User">SELECT * FROM user WHERE name LIKE '%' || #{name} || '%' ESCAPE '\'</select>
注意事项:
||连接符UPPER()函数)
<!-- SQL Server实现 --><select id="findByLikeSQLServer" resultType="User">SELECT * FROM user WHERE name LIKE '%' + #{name} + '%' ESCAPE '\'</select>
特性:
+连接符TOP分页#{}参数绑定,禁止${}拼接keyword)<choose>实现数据库方言适配databaseId特性问题1:中文模糊查询失效
-- 解决方案:设置正确的字符集和排序规则ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
问题2:通配符被转义
// 解决方案:自定义类型处理器处理转义字符public class LikeParameterHandler implements TypeHandler<String> {@Overridepublic void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {if (parameter != null) {parameter = parameter.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_");}ps.setString(i, parameter);}}
问题3:多字段模糊查询
<!-- 多字段模糊查询示例 --><select id="searchMultiField" resultType="User">SELECT * FROM userWHERE (name LIKE CONCAT('%', #{keyword}, '%')OR email LIKE CONCAT('%', #{keyword}, '%')OR phone LIKE CONCAT('%', #{keyword}, '%'))</select>
通过系统掌握这些技术要点和实践方案,开发者可以写出既安全又高效的MyBatis模糊查询代码,有效避免SQL注入风险,同时提升查询性能。在实际项目中,建议结合具体业务场景选择最适合的实现方式,并建立完善的输入验证和日志审计机制。