使用Mybatis-Plus中的模糊查询(like)时,当查询关键字中包括有_、\、%时,查询关键字无效,该怎么解决,解决方式有两个,一个自定义拦截器,拦截所有模糊查询语句,并将特殊符号转义;第二中方式就是定义一个函数,每次like查询,都将有特殊字符的字符串中所有特殊字符全部转移后返回新的转义后的字符串,再进行查询;

先说最直接简单的方法(定义函数)

将每个模糊查询的值都使用该方法进行转义

public static String escapeStr(String str) {
    if (StringUtils.isEmpty(str)) {
        str = str.replaceAll("\\\\", "\\\\\\\\");
        str = str.replaceAll("_", "\\\\_");
        str = str.replaceAll("%", "\\\\%");
    }
    return str;
}

复杂的方式(自定义拦截器)

自定义拦截器

/**
 * 自定义拦截器方法,处理模糊查询中包含特殊字符(_、%、\)
 *
 * @author Create by zhaowl at 2021/3/3 14:52
 */
public class MybatisPlusInterceptor implements Interceptor {

    private static final String LIKE_KEY = " like ";

    private static final String QUESTION_KEY = "?";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 拦截sql
        Object[] args = invocation.getArgs();
        MappedStatement statement = (MappedStatement) args[0];
        Object parameterObject = args[1];
        BoundSql boundSql = statement.getBoundSql(parameterObject);
        String sql = boundSql.getSql();
        // 处理特殊字符
        modifyLikeSql(sql, parameterObject, boundSql);
        // 返回
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    public static void modifyLikeSql(String sql, Object parameterObject, BoundSql boundSql) {
        if (!(parameterObject instanceof HashMap)) {
            return;
        }
        if (!sql.toLowerCase().contains(LIKE_KEY) || !sql.toLowerCase().contains(QUESTION_KEY)) {
            return;
        }
        // 获取关键字的个数(去重)
        String[] strList = sql.split("\\?");
        Set<String> keyNames = new HashSet<>();
        for (int i = 0; i < strList.length; i++) {
            if (strList[i].toLowerCase().contains(LIKE_KEY)) {
                String keyName = boundSql.getParameterMappings().get(i).getProperty();
                keyNames.add(keyName);
            }
        }
        // 对关键字进行特殊字符“清洗”,如果有特殊字符的,在特殊字符前添加转义字符(\)
        for (String keyName : keyNames) {
            HashMap<String, Object> parameter = JSONObject.parseObject(JSONObject.toJSONString(parameterObject), new TypeReference<HashMap<String, Object>>() {
            });
            if (keyName.contains("ew.paramNameValuePairs.") && sql.toLowerCase().contains(" like ?")) {
                // 第一种情况:在业务层进行条件构造产生的模糊查询关键字
                QueryWrapper<Object> wrapper = JSONObject.parseObject(JSONObject.toJSONString(parameter.get("ew")), new TypeReference<QueryWrapper<Object>>() {
                });
                parameter = (HashMap<String, Object>) wrapper.getParamNameValuePairs();
                String[] keyList = keyName.split("\\.");
                // 截取字符串之后,获取第三个,即为参数名
                Object a = parameter.get(keyList[2]);
                if (a instanceof String && (a.toString().contains("_") || a.toString().contains("\\") || a.toString()
                        .contains("%"))) {
                    parameter.put(keyList[2],
                            "%" + escapeStr(a.toString().substring(1, a.toString().length() - 1)) + "%");
                }
            } else if (!keyName.contains("ew.paramNameValuePairs.") && sql.toLowerCase().contains(" like ?")) {
                // 第二种情况:未使用条件构造器,但是在service层进行了查询关键字与模糊查询符`%`手动拼接
                Object a = parameter.get(keyName);
                if (a instanceof String && (a.toString().contains("_") || a.toString().contains("\\") || a.toString().contains("%"))) {
                    parameter.put(keyName, "%" + escapeStr(a.toString().substring(1, a.toString().length() - 1)) + "%");
                }
            } else {
                // 第三种情况:在Mapper类的注解SQL中进行了模糊查询的拼接
                Object a = parameter.get(keyName);
                if (a instanceof String && (a.toString().contains("_") || a.toString().contains("\\") || a.toString()
                        .contains("%"))) {
                    parameter.put(keyName, escapeStr(a.toString()));
                }
            }
        }
    }

    public static String escapeStr(String str) {
        if (StringUtils.isEmpty(str)) {
            str = str.replaceAll("\\\\", "\\\\\\\\");
            str = str.replaceAll("_", "\\\\_");
            str = str.replaceAll("%", "\\\\%");
        }
        return str;
    }

}

将自定义拦截器配置到MybatisPlus配置类中

/**
 * @author Create by zhaowl at 2021/3/3 14:52
 */
@Configuration
public class MyBatisPlusConfiguration {

    /**
     * 分页
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    /**
     * 自定义拦截器
     */
    @Bean
    public MybatisPlusInterceptor myInterceptor() {
        MybatisPlusInterceptor sql = new MybatisPlusInterceptor();
        return sql;
    }
}

特别注意

数据库字段排序规则为utf8_unicode_ci时,对于特殊符号的模糊查询失效,
所以,必须设置数据库字段排序规则为utf8_general_ci或utf8mb4_general_ci

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐