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字符串转义内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
解决springboot报错找不到自动注入的service问题
这篇文章主要介绍了解决springboot报错找不到自动注入的service问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2021-08-08
最新评论