mybatis plus like查询 %_处理

如果我们数据库中存的字段包含有"%_"这两个like查询的通配符,那么在查询的时候把"%_"当作关键字是查询不出来的,因为mybatis会把这两个字符当作通配符。解决方法是要能加转义字符

1.定义一个拦截器,如果要查询的字符串中包含"%_"则增加一个转义字符

package com.booway.application;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.xmltags.DynamicContext;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

@Component
@Intercepts(
{ @Signature(type = Executor.class, method = "query", args =
        { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),
        @Signature(type = Executor.class, method = "query", args =
        { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }) })
public class QueryStringEscapeInterceptor implements Interceptor
{
    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final String ROOT_SQL_NODE = "sqlSource.rootSqlNode";

    @Override
    public Object intercept(Invocation invocation) throws Throwable
    {
        Object parameter = invocation.getArgs()[1];
        MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
        MetaObject metaMappedStatement = MetaObject.forObject(statement, DEFAULT_OBJECT_FACTORY,
                DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
        BoundSql boundSql = statement.getBoundSql(parameter);
        if (metaMappedStatement.hasGetter(ROOT_SQL_NODE))
        {
            SqlNode sqlNode = (SqlNode) metaMappedStatement.getValue(ROOT_SQL_NODE);
            getBoundSql(statement.getConfiguration(), boundSql.getParameterObject(), sqlNode);
        }
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties)
    {
    }

    public static BoundSql getBoundSql(Configuration configuration, Object parameterObject, SqlNode sqlNode)
    {
        DynamicContext context = new DynamicContext(configuration, parameterObject);
        sqlNode.apply(context);
        String countextSql = context.getSql();
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
        Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
        String sql = modifyLikeSql(countextSql, parameterObject);
        SqlSource sqlSource = sqlSourceParser.parse(sql, parameterType, context.getBindings());

        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        for (Map.Entry<String, Object> entry : context.getBindings().entrySet())
        {
            boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
        }
        return boundSql;
    }

    public static String modifyLikeSql(String sql, Object parameterObject)
    {
        if (!sql.toLowerCase().contains("like"))
        {
            return sql;
        }
        String reg = "\\bLIKE\\b.*\\#\\{\\b.*\\}";
        Pattern pattern = Pattern.compile(reg, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(sql);

        List<String> replaceFiled = new ArrayList<String>();

        while (matcher.find())
        {
            int n = matcher.groupCount();
            for (int i = 0; i <= n; i++)
            {
                String output = matcher.group(i);
                if (output != null)
                {
                    String key = getParameterKey(output);
                    if (replaceFiled.indexOf(key) < 0)
                    {
                        replaceFiled.add(key);
                    }
                }
            }
        }
        // 修改参数
        MetaObject metaObject = MetaObject.forObject(parameterObject, DEFAULT_OBJECT_FACTORY,
                DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
        for (String key : replaceFiled)
        {
            Object val = metaObject.getValue(key);
            if (val != null && val instanceof String && (val.toString().contains("%") || val.toString().contains("_")))
            {
                val = val.toString().replaceAll("%", "/%").replaceAll("_", "/_");
                metaObject.setValue(key, val);
            }
        }
        return sql;
    }

    private static String getParameterKey(String input)
    {
        String key = "";
        String[] temp = input.split("#");
        if (temp.length > 1)
        {
            key = temp[1];
            key = key.replace("{", "").replace("}", "").split(",")[0];
        }
        return key.trim();
    }

}

2. 对面的查询mapper like后面要加escape '/'

<select id="selectProjectNameList" parameterType="BwProject" resultType="String">
        select PROJECT_NAME  from bw_project
        <where>
            <if test="projectName != null and projectName != ''">
                AND PROJECT_NAME like concat('%', #{projectName},'%') ESCAPE '/'
            </if>
             <if test="authConstructionUnit != null and authConstructionUnit != ''">
                and construction_unit = #{authConstructionUnit}
             </if>
        </where>
        order by CREATE_TIME desc
    </select>

引用 https://www.cnblogs.com/Gyoung/p/5876632.html

这里有点区别的是  Executor接口 有两个 query方法 所以我的代码中 添加了 这样两个 query方法都会被拦截到

@Signature(type = Executor.class, method = "query", args =
        { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class })

记得加入@Component 注解

 

posted @ 2020-08-14 09:22  closeIt  阅读(8217)  评论(0编辑  收藏  举报