测试代码:
- DruidDataSource dataSource = new DruidDataSource();
- dataSource.setUrl("xxx");
- dataSource.setUsername("xxx");
- dataSource.setPassword("xxx");
- dataSource.setFilters("slf4j");
- dataSource.setValidationQuery("SELECT 1");
- dataSource.setTestOnBorrow(true);
- dataSource.setTestWhileIdle(true);
-
- Connection connection = dataSource.getConnection();
-
- PreparedStatement stmt = connection.prepareStatement("select * from tb_order where id=?");
- stmt.setInt(1,1);
- stmt.execute();
在配置输出可执行的SQL之前,看下Druid的日志输出:

SQL占位符和参数分开打印
Java启动参数配置方式
-Ddruid.log.stmt.executableSql=true
logFilter参数直接配置:
- <bean id="log-filter" class="com.alibaba.druid.filter.logging.Log4jFilter">
- <property name="statementExecutableSqlLogEnable" value="true" />
- </bean>
在配置输出可执行的SQL之后,看下Druid的日志输出:

打印出了可执行的SQL
调用栈是DruidPooledPreparedStatement.execute->StatementProxyImpl.execute-> FilterChainImpl.preparedStatement_execute->FilterEventAdapter.preparedStatement_execute(所有Filter都继承了FilterEventAdapter)-> LogFilter.statementExecuteAfter->LogFilter.logExecutableSql。源码如下:
- // DruidPooledPreparedStatement.execute
- public boolean execute() throws SQLException {
- checkOpen();
-
- incrementExecuteCount();
- transactionRecord(sql);
-
- oracleSetRowPrefetch();
-
- conn.beforeExecute();
- try {
- //这个stmt是一个StatementProxyImpl实例
- return stmt.execute();
- } catch (Throwable t) {
- errorCheck(t);
-
- throw checkException(t);
- } finally {
- conn.afterExecute();
- }
- }
StatementProxyImpl.execute:
- // StatementProxyImpl.execute
- public boolean execute() throws SQLException {
- updateCount = null;
- lastExecuteSql = sql;
- lastExecuteType = StatementExecuteType.Execute;
- lastExecuteStartNano = -1L;
- lastExecuteTimeNano = -1L;
- //调用过滤器链的preparedStatement_execute
- firstResultSet = createChain().preparedStatement_execute(this);
- return firstResultSet;
- }
-
FilterChainImpl.preparedStatement_execute:
- public boolean preparedStatement_execute(PreparedStatementProxy statement) throws SQLException {
- if (this.pos < filterSize) {
- // 调用过滤器的preparedStatement_execute方法
- return nextFilter().preparedStatement_execute(this, statement);
- }
- return statement.getRawObject().execute();
- }
所有过滤器都继承了FilterEventAdapter,看名字是个和事件有关的类,这个父类里面实现了preparedStatement_execute方法:
- // FilterEventAdapter.preparedStatement_execute
- public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException {
- try {
- statementExecuteBefore(statement, statement.getSql());
- // 递归调用配置的所有过滤器的preparedStatement_execute方法,直到真正执行完statement
- boolean firstResult = chain.preparedStatement_execute(statement);
- // 在执行完statement后执行,这是个空方法,具体的逻辑在子类里面实现,即LogFilter
- this.statementExecuteAfter(statement, statement.getSql(), firstResult);
-
- return firstResult;
-
- } catch (SQLException error) {
- statement_executeErrorAfter(statement, statement.getSql(), error);
- throw error;
- } catch (RuntimeException error) {
- statement_executeErrorAfter(statement, statement.getSql(), error);
- throw error;
- } catch (Error error) {
- statement_executeErrorAfter(statement, statement.getSql(), error);
- throw error;
- }
-
- }
LogFilter.statementExecuteAfter方法:
- protected void statementExecuteAfter(StatementProxy statement, String sql, boolean firstResult) {
- // 打印可执行的SQL
- logExecutableSql(statement, sql);
- // 统计SQL执行的时间
- if (statementExecuteAfterLogEnable && isStatementLogEnabled()) {
- statement.setLastExecuteTimeNano();
- double nanos = statement.getLastExecuteTimeNano();
- double millis = nanos / (1000 * 1000);
-
- statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed. "
- + millis + " millis. " + sql);
- }
- }
LogFilter.logExecutableSql:
- private void logExecutableSql(StatementProxy statement, String sql) {
- // 没有配置输出可执行SQL,直接返回
- if ((!isStatementExecutableSqlLogEnable()) || !isStatementLogEnabled()) {
- return;
- }
- // 获取SQL参数数量
- int parametersSize = statement.getParametersSize();
- if (parametersSize == 0) {
- statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed. "
- + sql);
- return;
- }
- // 获取SQL的参数,存到parameters中
- 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);
- }
-
- DbType dbType = DbType.of(statement.getConnectionProxy().getDirectDataSource().getDbType());
- // 最终由SQLUtils根据参数列表和预执行SQL,转换为可执行SQL
- String formattedSql = SQLUtils.format(sql, dbType, parameters, this.statementSqlFormatOption);
- // statementLog是个抽象方法,由具体的日志过滤器类实现(如Slf4jLogFilter)
- statementLog("{conn-" + statement.getConnectionProxy().getId() + ", " + stmtId(statement) + "} executed. "
- + formattedSql);
- }
输出可执行SQL在日常排查SQL执行错误还是很实用的。其原理是在PreparedStatement.execute执行之后,调用SQLUtils.format打印出可执行的SQL。FilterEventAdapter这个类很关键,它会在SQL执行之前或者之后,调用扩展的处理,具体的处理逻辑又委派给子类实现。