Mybatis 输出SQL预编译后的语句及输出可执行的SQL语句

本文介绍了如何通过Mybatis拦截器和Druid过滤器获取预编译SQL,并展示了实现过程。Mybatis拦截器用于获取带问号的不可执行SQL,而Druid过滤器则能输出实际执行的SQL语句。执行顺序为Mybatis拦截器先执行,随后是Druid过滤器。
摘要由CSDN通过智能技术生成

环境

Mybatis、Druid(DataSource)

原因

业务需要及想进一步了解Mybatis知识。
下面就只写怎么做了,原理就一步一步去debug吧,自己动手能了解更多。

输出SQL预编译语句

共有两个方式能获取预编译后的语句:Mybatis拦截器、druid过滤器

Mybatis拦截器
@Slf4j
@Intercepts({
    @Signature(type = Executor.class, method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SqlOutputInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // invocation.getArgs() 的值是 @Signature 注解里面的 args 列表
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object[] parameterObject = invocation.getArgs();
        BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
        Configuration configuration = mappedStatement.getConfiguration();
        // 获取未处理的 SQL 字符串
        String sql = boundSql.getSql();
        // 若想看真正执行的SQL语句,详情请看 DefaultParameterHandler.java 的 setParameters(PreparedStatement ps)
        // 输出编译后的语句,带 “?” 的不可执行语句
        log.info(sql);
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}

注意的两个点:
1、BoundSql 存储了预编译后的 sql,是带 “?” 的不可直接执行的sql。
2、可执行的sql语句其实在mybatis发送往数据库前就已经拼好了,但数据是以 byte[] 方式存储的,且 Encoding 是 “utf-8” 存储的。原理上,可以先获取这个 byte[] 以及 Encoding。然后 new String(byte[], Encoding) 的方式直接获取可执行的 sql。

Druid(DataSource) 过滤器
@Slf4j
public class SqlOutputFilter extends FilterEventAdapter {
    @Override
    public void init(DataSourceProxy dataSource) {
        super.init(dataSource);
        log.info("初始化SqlOutputFilter。");
    }
    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {
        super.statementExecuteAfter(statement, sql, result);
        log.info("自定义过滤器,在执行操作后执行该方法,输出SQL={}", sql);
        int parametersSize = statement.getParametersSize();
        String formattedSql = sql;
        if (parametersSize > 0) {
            List<Object> parameters = new ArrayList<>(parametersSize);
            for (int i = 0; i < parametersSize; i++) {
                JdbcParameter parameter = statement.getParameter(i);
                parameters.add(parameter != null ? parameter.getValue() : null);
            }
            String dbType = statement.getConnectionProxy().getDirectDataSource().getDbType();
            formattedSql = SQLUtils.format(sql, dbType, parameters);
        }
        log.info("执行SQL语句,SQL={}", formattedSql);
    }
}

这个是参考了 Druid 的 LogFilter ,直接调用SQLUtils.format(String sql, String dbType, List parameters) 来获取可执行的 sql 语句。

执行顺序

拦截器跟过滤器都是递归方式执行:
正向:Mybatis 拦截器 -> Druid 过滤器
反向:Druid 过滤器 -> Mybatis 拦截器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值