我的编程空间,编程开发者的网络收藏夹
学习永远不晚

mybatis/mybatis-plus模糊查询语句特殊字符转义拦截器的实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

mybatis/mybatis-plus模糊查询语句特殊字符转义拦截器的实现

mybatis/mybatis-plus模糊查询语句特殊字符转义拦截器

在开发中,我们通常会遇到这样的情况。用户在录入信息是录入了‘%',而在查询时无法精确匹配‘%'。究其原因,‘%'是MySQL的关键字,如果我们想要精确匹配‘%',那么需要对其进行转义。

1.使用mybatis提供的拦截器拦截所有的查询请求。

具体实现在代码中均有注释


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.*;


@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
@Slf4j
public class MybatisLikeSqlInterceptor implements Interceptor {

    
    private final static String SQL_LIKE = " like ";

    
    private final static String SQL_PLACEHOLDER = "?";

    
    private final static String SQL_PLACEHOLDER_REGEX = "\\?";

    
    private static Map<Class, AbstractLikeSqlConverter> converterMap = new HashMap<>(4);

    static {
        converterMap.put(Map.class, new MapLikeSqlConverter());
        converterMap.put(Object.class, new ObjectLikeSqlConverter());
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement statement = (MappedStatement) args[0];
        Object parameterObject = args[1];
        BoundSql boundSql = statement.getBoundSql(parameterObject);
        String sql = boundSql.getSql();
        this.transferLikeSql(sql, parameterObject, boundSql);
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties arg0) {

    }

    
    private void transferLikeSql(String sql, Object parameterObject, BoundSql boundSql) {
        if (!isEscape(sql)) {
            return;
        }
        sql = sql.replaceAll(" {2}", " ");
        // 获取关键字的个数(去重)
        Set<String> fields = this.getKeyFields(sql, boundSql);
        if (fields == null) {
            return;
        }
        // 此处可以增强,不止是支持Map对象,Map对象仅用于传入的条件为Map或者使用@Param传入的对象被Mybatis转为的Map
        AbstractLikeSqlConverter converter;
        // 对关键字进行特殊字符“清洗”,如果有特殊字符的,在特殊字符前添加转义字符(\)
        if (parameterObject instanceof Map) {
            converter = converterMap.get(Map.class);
        } else {
            converter = converterMap.get(Object.class);
        }
        converter.convert(sql, fields, parameterObject);
    }

    
    private boolean isEscape(String sql) {
        return this.hasLike(sql) && this.hasPlaceholder(sql);
    }

    
    private boolean hasLike(String str) {
        if (StringUtils.isBlank(str)) {
            return false;
        }
        return str.toLowerCase().contains(SQL_LIKE);
    }

    
    private boolean hasPlaceholder(String str) {
        if (StringUtils.isBlank(str)) {
            return false;
        }
        return str.toLowerCase().contains(SQL_PLACEHOLDER);
    }

    
    private Set<String> getKeyFields(String sql, BoundSql boundSql) {
        String[] params = sql.split(SQL_PLACEHOLDER_REGEX);
        Set<String> fields = new HashSet<>();
        for (int i = 0; i < params.length; i++) {
            if (this.hasLike(params[i])) {
                String field = boundSql.getParameterMappings().get(i).getProperty();
                fields.add(field);
            }
        }
        return fields;
    }
}

2.定义SQL语句转义模板,分别对Map和Object对象进行处理

2.1 定义AbstractLikeSqlConverter


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;


@Slf4j
public abstract class AbstractLikeSqlConverter<T> {

    
    private final static String LIKE_SQL_KEY = "%";

    
    private final static String[] ESCAPE_CHAR = new String[]{LIKE_SQL_KEY, "_", "\\"};

    
    private final static String MYBATIS_PLUS_LIKE_SQL = " like ?";

    
    private final static String MYBATIS_PLUS_WRAPPER_PREFIX = "ew.paramNameValuePairs.";

    
    final static String MYBATIS_PLUS_WRAPPER_KEY = "ew";

    
    final static String MYBATIS_PLUS_WRAPPER_SEPARATOR = ".";

    
    final static String MYBATIS_PLUS_WRAPPER_SEPARATOR_REGEX = "\\.";

    
    final static String REPLACED_LIKE_KEYWORD_MARK = "replaced.keyword";

    
    public void convert(String sql, Set<String> fields, T parameter) {
        for (String field : fields) {
            if (this.hasMybatisPlusLikeSql(sql)) {
                if (this.hasWrapper(field)) {
                    // 第一种情况:在业务层进行条件构造产生的模糊查询关键字,使用QueryWrapper,LambdaQueryWrapper
                    this.transferWrapper(field, parameter);
                } else {
                    // 第二种情况:未使用条件构造器,但是在service层进行了查询关键字与模糊查询符`%`手动拼接
                    this.transferSelf(field, parameter);
                }
            } else {
                // 第三种情况:在Mapper类的注解SQL中进行了模糊查询的拼接
                this.transferSplice(field, parameter);
            }
        }
    }

    
    public abstract void transferWrapper(String field, T parameter);

    
    public abstract void transferSelf(String field, T parameter);

    
    public abstract void transferSplice(String field, T parameter);

    
    String escapeChar(String before) {
        if (StringUtils.isNotBlank(before)) {
            before = before.replaceAll("\\\\", "\\\\\\\\");
            before = before.replaceAll("_", "\\\\_");
            before = before.replaceAll("%", "\\\\%");
        }
        return before;
    }

    
    boolean hasEscapeChar(Object obj) {
        if (!(obj instanceof String)) {
            return false;
        }
        return this.hasEscapeChar((String) obj);
    }

    
    void resolveObj(String field, Object parameter) {
        if (parameter == null || StringUtils.isBlank(field)) {
            return;
        }
        try {
            PropertyDescriptor descriptor = new PropertyDescriptor(field, parameter.getClass());
            Method readMethod = descriptor.getReadMethod();
            Object param = readMethod.invoke(parameter);
            if (this.hasEscapeChar(param)) {
                Method setMethod = descriptor.getWriteMethod();
                setMethod.invoke(parameter, this.escapeChar(param.toString()));
            } else if (this.cascade(field)) {
                int index = field.indexOf(MYBATIS_PLUS_WRAPPER_SEPARATOR) + 1;
                this.resolveObj(field.substring(index), param);
            }
        } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
            log.error("反射 {} 的 {} get/set方法出现异常", parameter, field, e);
        }
    }

    
    boolean cascade(String field) {
        if (StringUtils.isBlank(field)) {
            return false;
        }
        return field.contains(MYBATIS_PLUS_WRAPPER_SEPARATOR) && !this.hasWrapper(field);
    }

    
    private boolean hasMybatisPlusLikeSql(String sql) {
        if (StringUtils.isBlank(sql)) {
            return false;
        }
        return sql.toLowerCase().contains(MYBATIS_PLUS_LIKE_SQL);
    }

    
    private boolean hasWrapper(String field) {
        if (StringUtils.isBlank(field)) {
            return false;
        }
        return field.contains(MYBATIS_PLUS_WRAPPER_PREFIX);
    }

    
    private boolean hasEscapeChar(String str) {
        if (StringUtils.isBlank(str)) {
            return false;
        }
        for (String s : ESCAPE_CHAR) {
            if (str.contains(s)) {
                return true;
            }
        }
        return false;
    }
}

2.2 定义MapLikeSqlConverter处理Map类型参数的情况


import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;

import java.util.Map;
import java.util.Objects;


public class MapLikeSqlConverter extends AbstractLikeSqlConverter<Map> {

    @Override
    public void transferWrapper(String field, Map parameter) {
        AbstractWrapper wrapper = (AbstractWrapper) parameter.get(MYBATIS_PLUS_WRAPPER_KEY);
        parameter = wrapper.getParamNameValuePairs();
        String[] keys = field.split(MYBATIS_PLUS_WRAPPER_SEPARATOR_REGEX);
        // ew.paramNameValuePairs.param1,截取字符串之后,获取第三个,即为参数名
        String paramName = keys[2];
        String mapKey = String.format("%s.%s", REPLACED_LIKE_KEYWORD_MARK, paramName);
        if (parameter.containsKey(mapKey) && Objects.equals(parameter.get(mapKey), true)) {
            return;
        }
        if (this.cascade(field)) {
            this.resolveCascadeObj(field, parameter);
        } else {
            Object param = parameter.get(paramName);
            if (this.hasEscapeChar(param)) {
                String paramStr = param.toString();
                parameter.put(keys[2], String.format("%%%s%%", this.escapeChar(paramStr.substring(1, paramStr.length() - 1))));
            }
        }
        parameter.put(mapKey, true);
    }

    @Override
    public void transferSelf(String field, Map parameter) {
        if (this.cascade(field)) {
            this.resolveCascadeObj(field, parameter);
            return;
        }
        Object param = parameter.get(field);
        if (this.hasEscapeChar(param)) {
            String paramStr = param.toString();
            parameter.put(field, String.format("%%%s%%", this.escapeChar(paramStr.substring(1, paramStr.length() - 1))));
        }
    }

    @Override
    public void transferSplice(String field, Map parameter) {
        if (this.cascade(field)) {
            this.resolveCascadeObj(field, parameter);
            return;
        }
        Object param = parameter.get(field);
        if (this.hasEscapeChar(param)) {
            parameter.put(field, this.escapeChar(param.toString()));
        }
    }

    
    private void resolveCascadeObj(String field, Map parameter) {
        int index = field.indexOf(MYBATIS_PLUS_WRAPPER_SEPARATOR);
        Object param = parameter.get(field.substring(0, index));
        if (param == null) {
            return;
        }
        this.resolveObj(field.substring(index + 1), param);
    }
}

2.3 定义ObjectLikeSqlConverter处理Object类型参数的情况


import lombok.extern.slf4j.Slf4j;


@Slf4j
public class ObjectLikeSqlConverter extends AbstractLikeSqlConverter<Object> {

    @Override
    public void transferWrapper(String field, Object parameter) {
        // 尚未发现这种情况
    }

    @Override
    public void transferSelf(String field, Object parameter) {
        // 尚未发现这种情况
    }

    @Override
    public void transferSplice(String field, Object parameter) {
        this.resolveObj(field, parameter);
    }
}

3. 注册到Spring中


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class MybatisLikeSqlConfig {

    @Bean
    public MybatisLikeSqlInterceptor mybatisSqlInterceptor() {
        return new MybatisLikeSqlInterceptor();
    }
}

到此,即可进行关键字的精确匹配。

到此这篇关于mybatis/mybatis-plus模糊查询语句特殊字符转义拦截器的实现的文章就介绍到这了,更多相关mybatis-plus模糊查询特殊字符转义拦截器内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

mybatis/mybatis-plus模糊查询语句特殊字符转义拦截器的实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

mybatis/mybatis-plus模糊查询语句特殊字符转义拦截器的实现方法是什么

本篇内容主要讲解“mybatis/mybatis-plus模糊查询语句特殊字符转义拦截器的实现方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“mybatis/mybatis-plus模糊
2023-06-25

Mybatis特殊字符转义查询实现

本文主要介绍了Mybatis特殊字符转义查询实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-02-03

mybatis中like模糊查询特殊字符报错怎么实现转义处理

这篇文章给大家分享的是有关mybatis中like模糊查询特殊字符报错怎么实现转义处理的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。like模糊查询特殊字符报错转义处理方案1 2023-06-26

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录