在MYSQL中,使用LIKE关键字进行模糊匹配时候,特殊字符是需要转义的,如下: \
转义成 \\
, _
转义成 \_
, %
转义成 \%
。
在mybatis和mybatis-plus中,没有提供相关的方法来自动转义。其实这是符合逻辑的,转义这种事情该交给开发者自己去处理,官方框架不去入侵这种逻辑代码也是为了更好的灵活性。
在业务开发的环境中,往往会有测试提出这样的bug:“输入特殊字符进行筛选,没有过滤掉对应的数据”。像这种问题其实本质上是不应该存在的,在根源上就应该切除掉,就是说禁止输入特殊字符进行筛选。但往往测试和产品他们根本不懂技术,你也别废口舌去和他们争论了。那么我们就来看看如何处理关于LIKE特殊字符转义问题。
方案一:实现mybatis的插件接口
我们知道mybatis提供了插件接口方便开发者进行二次开发。mybatis-plus对mybatis进行了增强,在mybatis-plus中需要实现InnerInterceptor接口进行拦截。如果只是引入的mybatis,则实现Interceptor接口进行拦截。注意一下:
该方案对 like xxx%
和 like %xxx
的查询方式会有一定的局限性。如:
用户输入 = %a 时,业务按照 like xxx%
的语义查询,是查询xxx开头的数据。正常该转义成 like \%a%
。
用户输入 = a% 时,业务按照 like %xxx
的语义查询,是查询xxx结尾的数据。正常该转义成 like %a\%
。
但程序在转换时,依然会按照 like %xxx%
的语义去转义。这是不可避免的,因为这确实是歧义的地方。 程序解析 like %a%
时,无法完全地确定哪一部分是用户输入的,所以只有按照标准的 %xxx%
来进行解析转义。
不过在一般的业务查询中,左LIKE 和 右LIKE 很少用到,可以不用考虑此情况。
下面是实现了mybatis-plus的拦截接口,代码如下:
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
/**
* @author shaohuailin
* @date 2023/10/8
*/
@Slf4j
public class LikeEscapeInnerInterceptor implements InnerInterceptor {
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
escapeLike(boundSql);
}
/**
* like条件中若有关键字,则进行转义
*
* @param boundSql
*/
public