MyBatis-Plus模糊查询特殊字符串转义的实现

 更新时间:2024年06月23日 16:13:13   作者:广外摸鱼王  
使用MyBatis中的模糊查询时,当查询关键字中包括有_、\、%时,查询关键字失效,本文主要介绍了MyBatis-Plus模糊查询特殊字符串转义的实现,感兴趣的可以了解一下

问题描述

使用MyBatis中的模糊查询时,当查询关键字中包括有_\%时,查询关键字失效。例如需要查询出名称带下划线的用户,点击查询后却查出了全部用户。

解决方案

MyBatis-Plus会直接将用户的输入拼接到查询语句中,例如%_%,但我们实际需要的是%\_%,为了解决这个问题可以使用一个MyBatis-Plus的全局拦截器对特殊字符进行转义。下面是拦截器的源代码:

/**
 * 特殊字符转换 like 查询
 * 参数中的特殊查询字符  _  %  \
 * 注意事项 :
 * 1. 必须是在 分页拦截器之前执行 [注意拦截器的顺序]
 * 2. 当数据库字段排序规则为utf8_unicode_ci时,对于特殊符号的模糊查询失效,
 * 所以,必须设置数据库字段排序规则为utf8_general_ci或utf8mb4_general_ci
 *
 */
@Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
        RowBounds.class, ResultHandler.class}))
public class SpecialCharacterConversionLikeInnerInterceptor implements Interceptor {
    @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 String modifyLikeSql(String sql, Object parameterObject, BoundSql boundSql) {
        if (parameterObject instanceof HashMap) {
        } else {
            return sql;
        }
        if (!sql.toLowerCase().contains(" like ") || !sql.toLowerCase().contains("?")) {
            return sql;
        }
        // 获取关键字的个数(去重)
        String[] strList = sql.split("\\?");
        Set<String> keyNames = new HashSet<>();
        for (int i = 0; i < strList.length; i++) {
            if (strList[i].toLowerCase().contains(" like ")) {
                String keyName = boundSql.getParameterMappings().get(i).getProperty();
                keyNames.add(keyName);
            }
        }
        // 对关键字进行特殊字符“清洗”,如果有特殊字符的,在特殊字符前添加转义字符(\)
        for (String keyName : keyNames) {
            HashMap parameter = (HashMap) parameterObject;
            if (keyName.contains("ew.paramNameValuePairs.") && sql.toLowerCase().contains(" like ?")) {
                // 第一种情况:在业务层进行条件构造产生的模糊查询关键字
                AbstractWrapper wrapper = (AbstractWrapper) parameter.get("ew");

                parameter = (HashMap) wrapper.getParamNameValuePairs();

                String[] keyList = keyName.split("\\.");
                // ew.paramNameValuePairs.MPGENVAL1,截取字符串之后,获取第三个,即为参数名
                Object a = parameter.get(keyList[2]);
                if (a instanceof String && (a.toString().contains("_") || a.toString().contains("\\") || a.toString()
                        .contains("%"))) {
                    parameter.put(keyList[2],
                            "%" + escapeChar(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,
                            "%" + escapeChar(a.toString().substring(1, a.toString().length() - 1)) + "%");
                }
            } else {
                // 第三种情况:在Mapper类的注解SQL中进行了模糊查询的拼接
                updateValue(parameter, keyName);
            }
        }
        return sql;
    }

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

    /**
     * 从参数字典中取值
     * 取出keyName对应的value
     *
     * @param parameter 参数字典
     * @param keyName   key名称
     * @return value
     */
    private void updateValue(HashMap parameter, String keyName) {
        String v = "";
        List<String> sub_keys = Arrays.asList(keyName.split("\\."));
        if (sub_keys.size() == 1 && parameter.containsKey(keyName)) {
            v = parameter.get(keyName).toString();
            if ((v.contains("_") || v.contains("\\") || v.contains("%"))) {
                parameter.put(keyName, escapeChar(v));
            }
        } else if (sub_keys.size() == 2 && parameter.containsKey(sub_keys.get(0))) {
            HashMap bean = (HashMap) BeanUtil.beanToMap(parameter.get(sub_keys.get(0)));
            if (bean.containsKey(sub_keys.get(1))) {
                v = (String) bean.get(sub_keys.get(1));
                if ((v.contains("_") || v.contains("\\") || v.contains("%"))) {
                    v = escapeChar(v);
                }
                bean.put(sub_keys.get(1), v);
                parameter.put(sub_keys.get(0), bean);
            }
        } else {
            // 暂不支持复杂的Bean
            return;
        }
    }
}

该代码参考互联网代码,并对第三种情况进行了扩展,目前可以支持自定义类参数。

之后将拦截器添加到配置类即可

@Configuration
public class MybatisPlusConfig {
    /**
     * 模糊查询拦截器
     *
     * @return
     */
    @Bean
    public SpecialCharacterConversionLikeInnerInterceptor myInterceptor() {
        return new SpecialCharacterConversionLikeInnerInterceptor();
    }
}

参考连接:
https://www.jianshu.com/p/f4d3e6ffeee8 

到此这篇关于MyBatis-Plus模糊查询特殊字符串转义的实现的文章就介绍到这了,更多相关MyBatisPlus字符串转义内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • 解析Spring Cloud Bus消息总线

    解析Spring Cloud Bus消息总线

    这篇文章主要介绍了Spring Cloud Bus消息总线的介绍及使用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-10-10
  • Java使用POI生成Word文档简单代码示例

    Java使用POI生成Word文档简单代码示例

    Java POI是一个用于操作Microsoft Office格式文件的Java库,包括 Word、Excel和PowerPoint等文件,这篇文章主要给大家介绍了关于Java使用POI生成Word文档的相关资料,需要的朋友可以参考下
    2024-08-08
  • Spring定时服务QuartZ原理及代码案例

    Spring定时服务QuartZ原理及代码案例

    这篇文章主要介绍了Spring定时服务QuartZ原理及代码案例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • Java中的Stream流式操作大全

    Java中的Stream流式操作大全

    Java 8引入的Stream API提供了一种高效、简洁的集合数据处理方式,基于函数式编程思想,支持惰性求值、一次性消费、无状态/有状态操作和并行处理,本文通过实例代码介绍java中的Stream流式操作,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • idea中怎样创建并运行第一个java程序

    idea中怎样创建并运行第一个java程序

    这篇文章主要介绍了idea中怎样创建并运行第一个java程序问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • springboot配置logback日志管理过程详解

    springboot配置logback日志管理过程详解

    这篇文章主要介绍了springboot配置logback日志管理过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • java获取当前时间的四种方法代码实例

    java获取当前时间的四种方法代码实例

    这篇文章主要介绍了java获取当前时间的四种方法代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • 关于mybatis遇到Integer类型的参数时动态sql需要注意条件

    关于mybatis遇到Integer类型的参数时动态sql需要注意条件

    这篇文章主要介绍了关于mybatis遇到Integer类型的参数时动态sql需要注意条件,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java Redis Template批量查询指定键值对的实现

    Java Redis Template批量查询指定键值对的实现

    本文主要介绍了Java Redis Template批量查询指定键值对的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • logback 配置详解(推荐)

    logback 配置详解(推荐)

    这篇文章主要介绍了logback 配置详解(推荐),详细的介绍了logback的组成使用和配置,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11

最新评论