Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to intercept and change sql query dynamically in mybatis

Tags:

java

sql

mybatis

I use mybatis to perform sql queries in my project. I need to intercept sql query before executing to apply some changed dynamically. I've read about @Interseptors like this:

@Intercepts({@Signature(type= Executor.class, method = "query", args = {...})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}

And it really intercepts executions, but there is no way to change sql query since appropriate field is not writable. Should I build new instance of whole object manually to just replace sql query? Where is the right place to intercept query execution to change it dynamically? Thank.

like image 912
sochi Avatar asked Apr 22 '13 17:04

sochi


People also ask

How do I avoid SQL injection in MyBatis?

In general, you should consider using #{} for string substitution instead of ${} . This is because #{} causes Mybatis to create a preparedStatement which prevents SQLInjection compared to ${} which would inject unmodified string into SQL.

What is the difference between iBatis and MyBatis?

It is the same thing, a persistence framework! But until June 2010, iBatis was under Apache license and since then, the framework founders decided to move it to Google Code and they renamed it to MyBatis. The framework is still the same though, it just has a different name now.

Does MyBatis use JDBC?

Background information. MyBatis is a persistence framework for Java that supports custom SQL statements, stored procedures, and advanced mappings. MyBatis eliminates the need to use JDBC code, manually configure parameters, and retrieve result sets.


1 Answers

I hope it will help you:

@Intercepts( { @Signature(type = Executor.class, method = "query", args = {
        MappedStatement.class, Object.class, RowBounds.class,
        ResultHandler.class
    })
})
public class SelectCountSqlInterceptor2 implements Interceptor
{
    public static String COUNT = "_count";
    private static int MAPPED_STATEMENT_INDEX = 0;
    private static int PARAMETER_INDEX = 1;
    @Override
    public Object intercept(Invocation invocation) throws Throwable
    {
        processCountSql(invocation.getArgs());
        return invocation.proceed();
    }
    @SuppressWarnings("rawtypes")
    private void processCountSql(final Object[] queryArgs)
    {
        if (queryArgs[PARAMETER_INDEX] instanceof Map)
        {
            Map parameter = (Map) queryArgs[PARAMETER_INDEX];
            if (parameter.containsKey(COUNT))
            {
                MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX];
                BoundSql boundSql = ms.getBoundSql(parameter);
                String sql = ms.getBoundSql(parameter).getSql().trim();
                BoundSql newBoundSql = new BoundSql(ms.getConfiguration(),
                                                    getCountSQL(sql), boundSql.getParameterMappings(),
                                                    boundSql.getParameterObject());
                MappedStatement newMs = copyFromMappedStatement(ms,
                                        new OffsetLimitInterceptor.BoundSqlSqlSource(newBoundSql));
                queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
            }
        }
    }
    // see: MapperBuilderAssistant
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private MappedStatement copyFromMappedStatement(MappedStatement ms,
            SqlSource newSqlSource)
    {
        Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms
                .getId(), newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        // setStatementTimeout()
        builder.timeout(ms.getTimeout());
        // setParameterMap()
        builder.parameterMap(ms.getParameterMap());
        // setStatementResultMap()
        List<ResultMap> resultMaps = new ArrayList<ResultMap>();
        String id = "-inline";
        if (ms.getResultMaps() != null)
        {
            id = ms.getResultMaps().get(0).getId() + "-inline";
        }
        ResultMap resultMap = new ResultMap.Builder(null, id, Long.class,
                new ArrayList()).build();
        resultMaps.add(resultMap);
        builder.resultMaps(resultMaps);
        builder.resultSetType(ms.getResultSetType());
        // setStatementCache()
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }
    private String getCountSQL(String sql)
    {
        String lowerCaseSQL = sql.toLowerCase().replace("\n", " ").replace("\t", " ");
        int index = lowerCaseSQL.indexOf(" order ");
        if (index != -1)
        {
            sql = sql.substring(0, index);
        }
        return "SELECT COUNT(*) from ( select 1 as col_c " + sql.substring(lowerCaseSQL.indexOf(" from "))  + " )   cnt";
    }
    @Override
    public Object plugin(Object target)
    {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties)
    {
    }
}
like image 137
RookieCy Avatar answered Sep 21 '22 12:09

RookieCy