携程系Java服务中MySQL模糊查询需对%、_、'、双重转义,推荐ESCAPE方式;Java字符串和SQL解析各吃一层反斜杠,故"%\%%"才对应MySQL的"%%%";ESCAPE用!转义更可靠,工具类应顺序处理且优先转义转义符本身。

直接结论:携程系 Java 服务中用 MySQL 客户端做模糊查询时,若用户输入含 %、_、'、,不转义就会查出全表或漏数据;必须在拼 SQL 前对参数做双重转义(Java 字符串 + SQL 解析层),且推荐统一用 ESCAPE 方式而非硬写 %。
为什么 like '%%%' 在 Java 里经常失效
因为 Java 字符串本身会吃掉一层反斜杠。你写 "%\%%",编译后变成 "%%%",传给 MySQL 时只剩一个 %;而 MySQL 的 LIKE 解析器还要再吃一次转义,最终实际匹配的是字面量 % —— 也就是又变回通配符了。
- Java 源码写
"%\%%"→ JVM 解析为"%%%" - MySQL 收到
"%%%"→ LIKE 解析器把%当作转义,剩下"%%"→ 匹配任意内容 - 真正要让 MySQL 看到
"%\%%"(即两个反斜杠),Java 得写成"%\\%%"
用 ESCAPE 是更稳的写法
绕开反斜杠嵌套问题,显式声明转义符,语义清晰、调试友好。比如用 ! 作转义符,所有需要字面量匹配的字符前加 ! 即可:
SELECT * FROM region_info WHERE region LIKE '%!%%' ESCAPE '!';
对应 Java 代码里只需简单替换:
- 用户输入
"50%"→ 替换为"50!%" - 用户输入
"a_b"→ 替换为"a!_b" - 注意:转义符本身也要逃,比如用户真输
"!",就得写成"!!"
Java 工具类怎么写才不出错
别用正则 replaceAll 直接扫一遍 —— 它容易误伤已存在的转义序列。应该按顺序逐字符处理,且优先转义转义符本身:
- 先将
"\"替换为"\\\"(Java 字符串中表示"\\") - 再将
"%"替换为"\%" - 再将
"_"替换为"\_" - 最后将
"'"替换为"\'"
但更推荐封装成带 escapeChar 参数的方法,返回适配 ESCAPE 子句的字符串,例如:
public static String escapeForLike(String s, char escapeChar) {
if (s == null) return null;
StringBuilder sb = new StringBuilder();
for (char c : s.toCharArray()) {
if (c == escapeChar || c == '%' || c == '_') {
sb.append(escapeChar);
}
sb.append(c);
}
return sb.toString();
}容易被忽略的边界:索引失效和空值
即使转义正确,LIKE '%xxx%' 仍无法走索引;如果字段允许 NULL,LIKE 对 NULL 永远返回 FALSE,得显式加 OR column IS NULL。
另外,MySQL 8.0+ 默认排序规则(如 utf8mb4_0900_as_cs)下,大小写敏感,但 LIKE 仍默认不区分大小写;若业务要求严格匹配,得加 BINARY 或改用 REGEXP。



















