MySQL中有两个通配符:%,匹配任意多个字符;_,匹配任意一个字符。那么如果我想过滤出含有%的结果,直接写:
SELECT * FROM person WHERE name LIKE '%%k%';
是过滤不出来的。但是这样写:
SELECT * FROM person WHERE name LIKE '%\\%k%';
是可以的,这里\是转义字符。
普通字符串在进行转义的时候,几乎可以转义一切,但是%和_却除外,例如:
SELECT '\%';
查询结果为:
\%
但是like查询条件内的转义字符,实际上是进行了两次转义,第一次和普通字符串的转义一样,第二次则可以将\%和\_转义为%和_。
所以Sql:
SELECT * FROM person WHERE name LIKE '%\\%k%';
在经过两次转义之后,可以实现将通配符%像普通字符一样过滤查询结果。
那么如果我想让like查询条件的转义字符像普通字符一样过滤查询结果该怎样呢?经过上面的解释,我们知道like查询条件实际上是进行了两次转义,所以我们这样写:
SELECT * FROM person WHERE name LIKE '%\\\\k%';
在经过两次转义之后,就可以过滤\了。
以上,是对原生Sql的处理,我平时写java代码,于是在接着写java代码内的解决方案。
此时有一个场景,前端想按照姓名过滤,找人,但是我输入张,应该找到所有姓张的人,但是如果我输入\张、%张或者_张则不应该搜索出姓张的人。
按照上面的解释,我们知道在原生Sql中,将这三个字符转义成普通字符就可以了,那么在java中呢?
name = name.replaceAll("\\\\", "\\\\\\\\\\\\\\\\").replaceAll("%", "\\\\\\\\%").replaceAll("_", "\\\\\\\\_");
这是对原生方案的落实,需要单独说的只是replaceAll这个API在这个过程中的使用,在replaceAll中,实际上也进行了两次转义,所以你可以看到上面的代码中,将一个\替换为\\的话,第一个参数要写4个\,经过转义变为1个\,第二个参数要写16个\,经过转义后变为4个\\。
好啦,写完了。
备注:Hibernate的Hql对like条件的转义字符的处理,似乎和MySql有着不同的规则,在Hibernate中,需要将\替换成\\,将%替换成\%,将_替换成\_。