PrepareStatement输出完整SQL语句

为了防止SQL注入,我们通过采用PrepareStatement代替Statement。使用Mybatis的情况下就是使用 #{} 来答题 ${}

凡事有利必有弊,这样带来了安全性,但随之而来的是调试阶段的检测SQL正确性的繁琐。因为我们需要一个个将?替换为原始的值才能放到诸如plsql里去执行。

本文介绍如何在Druid中粗略解决这个问题。

前言

在现在的开发工作中,我们一般采用数据库连接池的方式来协助我们进行数据库的操作。而Druid作为国内非常知名的数据库连接池。其设计理念决定了解决我们上面提到的需求应该是一件非常轻松的事情。

原理

通常我们想要Druid输出相关执行的SQL语句,我们一般会进行如下配置:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        .....
        <property name="filters" value="slf4j"/>
        ......
    </bean>

跟随其配置,我们可以最终发现Slf4jLogFilter类。而其基类LogFilterlogExecutableSql正承担了我们所关心的任务。 而控制这个方法真正执行的正是 statementExecutableSqlLogEnable 字段的值,其默认为false, 所以我们需要启用它。

    <bean id="log-filter" class="com.alibaba.druid.filter.logging.Slf4jLogFilter">
        <property name="connectionLogEnabled" value="false"/>
        <property name="statementLogEnabled" value="false"/>
        <property name="resultSetLogEnabled" value="true"/>
        <!-- 启用 -->
        <property name="statementExecutableSqlLogEnable" value="true"/>
    </bean>

 <!-- 数据连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        .....
        <property name="filters" value="stat,wall"/>
        ......
        <property name="proxyFilters">
            <list>
                <!-- 载入 -->
                <ref bean="log-filter"/>
            </list>
        </property>
    </bean>

疑难问题

发现一个问题就是,当SQL语句出错时Druid是不会进行 ? 替换的。 所以我们需要进行一些魔改。

// 使用如下类代替默认的Slf4jLogFilter 进行Filter注册.
/*
    datasource.setProxyFilters(new ArrayList<Filter>() {
        private static final long serialVersionUID = 1L;

        {
            add(constructFilter());
        }
    });
*/
public class DruidSlf4jLoggerFilterEx extends Slf4jLogFilter {

    private static final Logger LOG = LoggerFactory
            .getLogger(DruidSlf4jLoggerFilterEx.class);

    // 将日志信息导入到我们要求的位置
    @Override
    protected void statementLogError(String message, Throwable error) {
        LOG.error(message, error);
        // super.statementLogError(message, error);
    }

    // 在SQL语句执行出错时, 依然进行 ? 替换
    @Override
    protected void statement_executeErrorAfter(StatementProxy statement,
            String sql, Throwable error) {
        if (!this.isStatementLogErrorEnabled()) {
            return;
        }

        if (!isStatementExecutableSqlLogEnable()) {
            statementLogError("{conn-" + statement.getConnectionProxy().getId()
                    + ", " + stmtId(statement) + "} execute error. " + sql,
                    error);
            return;
        }

        int parametersSize = statement.getParametersSize();
        if (parametersSize <= 0) {
            statementLogError("{conn-" + statement.getConnectionProxy().getId()
                    + ", " + stmtId(statement) + "} execute error. " + sql,
                    error);
        }

        final List<Object> parameters = new ArrayList<Object>(parametersSize);
        for (int i = 0; i < parametersSize; ++i) {
            JdbcParameter jdbcParam = statement.getParameter(i);
            parameters.add(jdbcParam != null ? jdbcParam.getValue() : null);
        }

        /*  Druid源码 
                String dbType = statement.getConnectionProxy().getDirectDataSource()
                        .getDbType();
                String formattedSql = SQLUtils.format(sql, dbType, parameters,
                        this.getStatementSqlFormatOption());
                statementLogError(
                        "{conn-" + statement.getConnectionProxy().getId() + ", "
                                + stmtId(statement) + "} execute error. "
                                + formattedSql, error);
        */

        final String formattedSql = transSql(parameters, sql);
        statementLogError(
                "{conn-" + statement.getConnectionProxy().getId() + ", "
                        + stmtId(statement) + "} execute error. "
                        + formattedSql, error);
    }

    private String transSql(List<Object> parameters, String sql) {

        if (sql.indexOf("?") < 0) {

            return sql;

        }

        for (int i = 0; i < parameters.size(); i++) {
            sql = sql.replaceFirst("\\?", parameters.get(i) != null ? "\'"
                    + parameters.get(i).toString() + "\'" : "NULL");
        }
        return sql;
    }   

    private String stmtId(StatementProxy statement) {
        StringBuffer buf = new StringBuffer();
        if (statement instanceof CallableStatementProxy) {
            buf.append("cstmt-");
        } else if (statement instanceof PreparedStatementProxy) {
            buf.append("pstmt-");
        } else {
            buf.append("stmt-");
        }
        buf.append(statement.getId());

        return buf.toString();
    }


}
  1. Druid搭配log4j2输出SQL语句和结果

猜你喜欢

转载自blog.csdn.net/lqzkcx3/article/details/79259117
今日推荐