Mybatis系列十一:SQL及执行结果日志输出

Mybatis系列十五:SQL及执行结果日志输出

概述

每个MappedStatement创建一个日志对象,可以控制每个运行时SQL语句的打印以及执行结果的打印。
日志Debug级别打印SQL语句:PreparedStatement(prepareStatement、prepareCall)创建时打印SQL语句,执行时打印参数值。Statement执行时打印SQL语句。
日志Trace级别打印执行结果。

一、MappedStatement

1.1 构建MappedStatement时指定日志

public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
     mappedStatement.configuration = configuration;
     // 命名空间.statementId即Mapper接口完全限定名.方法名
     mappedStatement.id = id;
     mappedStatement.sqlSource = sqlSource;
     mappedStatement.statementType = StatementType.PREPARED;
     mappedStatement.parameterMap = new ParameterMap.Builder(configuration, “defaultParameterMap”, null, new ArrayList()).build();
     mappedStatement.resultMaps = new ArrayList();
     mappedStatement.sqlCommandType = sqlCommandType;
     mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
     String logId = id;
// 配置日志前缀
     if (configuration.getLogPrefix() != null) {
      ** logId = configuration.getLogPrefix() + id;**
     }
// 每个MappedStatement(每个SQL语句)创建一个日志对象
    ** mappedStatement.statementLog = LogFactory.getLog(logId);**
     mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
   }

二、BaseExecutor

2.1 创建Connection

// SimpleExecutor、BatchExecutor、ReuseExecutor调用该方法获取Connection
protected Connection getConnection(Log statementLog) throws SQLException {
   Connection connection = transaction.getConnection();
   // MappedStatement开启Debug级别日志
   if (statementLog.isDebugEnabled()) {
// 具有日志打印功能的Connection
     return ConnectionLogger.newInstance(connection, statementLog, queryStack);
   } else {
     return connection;
   }
 }

三、ConnectionLogger

3.1 创建Connection代理实例

public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
   InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
   ClassLoader cl = Connection.class.getClassLoader();
   return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
 }

3.2 创建日志打印功能的Statement

public Object invoke(Object proxy, Method method, Object[] params)
     throws Throwable {
   try {
     if (Object.class.equals(method.getDeclaringClass())) {
       return method.invoke(this, params);
     }    
     if (“prepareStatement”.equals(method.getName())) {
       if (isDebugEnabled()) {
    // 日志Debug级别打印SQL语句
         debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
       }        
       PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
       stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
       return stmt;
     } else if (“prepareCall”.equals(method.getName())) {
       if (isDebugEnabled()) {
    // 日志Debug级别打印SQL语句
         debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
       }        
       PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
       stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
       return stmt;
     } else if (“createStatement”.equals(method.getName())) {
       Statement stmt = (Statement) method.invoke(connection, params);
       stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
       return stmt;
     } else {
       return method.invoke(connection, params);
     }
   } catch (Throwable t) {
     throw ExceptionUtil.unwrapThrowable(t);
   }
 }

四、StatementLogger

4.1 创建Statement代理实例

public static Statement newInstance(Statement stmt, Log statementLog, int queryStack) {
   InvocationHandler handler = new StatementLogger(stmt, statementLog, queryStack);
   ClassLoader cl = Statement.class.getClassLoader();
   return (Statement) Proxy.newProxyInstance(cl, new Class[]{Statement.class}, handler);
 }

4.2 创建日志打印功能的ResultSet

public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
   try {
     if (Object.class.equals(method.getDeclaringClass())) {
       return method.invoke(this, params);
     }

// execute、executeUpdate、executeQuery、addBatch
     if (EXECUTE_METHODS.contains(method.getName())) {
       if (isDebugEnabled()) {
         // 日志Debug级别打印SQL语句
        ** debug(" Executing: " + removeBreakingWhitespace((String) params[0]), true);**
       }
       if (“executeQuery”.equals(method.getName())) {
         ResultSet rs = (ResultSet) method.invoke(statement, params);
         return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
       } else {
         return method.invoke(statement, params);
       }
     } else if (“getResultSet”.equals(method.getName())) {
       ResultSet rs = (ResultSet) method.invoke(statement, params);
       return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
     } else {
       return method.invoke(statement, params);
     }
   } catch (Throwable t) {
     throw ExceptionUtil.unwrapThrowable(t);
   }
 }

五、PreparedStatementLogger

5.1 创建PreparedStatement代理实例

public static PreparedStatement newInstance(PreparedStatement stmt, Log statementLog, int queryStack) {
   InvocationHandler handler = new PreparedStatementLogger(stmt, statementLog, queryStack);
   ClassLoader cl = PreparedStatement.class.getClassLoader();
   return (PreparedStatement) Proxy.newProxyInstance(cl, new Class[]{PreparedStatement.class, CallableStatement.class}, handler);
 }

5.2 创建日志打印功能的ResultSet

public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
   try {
     if (Object.class.equals(method.getDeclaringClass())) {
       return method.invoke(this, params);
     }

// execute、executeUpdate、executeQuery、addBatch
     if (EXECUTE_METHODS.contains(method.getName())) {
       if (isDebugEnabled()) {
         // 日志Debug级别打印参数值
         debug("Parameters: " + getParameterValueString(), true);
       }
       clearColumnInfo();
       if (“executeQuery”.equals(method.getName())) {
         ResultSet rs = (ResultSet) method.invoke(statement, params);
         return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
       } else {
         return method.invoke(statement, params);
       }
     } else if (SET_METHODS.contains(method.getName())) {
       if (“setNull”.equals(method.getName())) {
         setColumn(params[0], null);
       } else {
         setColumn(params[0], params[1]);
       }
       return method.invoke(statement, params);
     } else if (“getResultSet”.equals(method.getName())) {
       ResultSet rs = (ResultSet) method.invoke(statement, params);
       return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
     } else if (“getUpdateCount”.equals(method.getName())) {
       int updateCount = (Integer) method.invoke(statement, params);
       if (updateCount != -1) {
         debug("   Updates: " + updateCount, false);
       }
       return updateCount;
     } else {
       return method.invoke(statement, params);
     }
   } catch (Throwable t) {
     throw ExceptionUtil.unwrapThrowable(t);
   }
 }

六、ResultSetLogger 日志TRACE级别打印执行结果

6.1 创建ResultSet代理实例

public static ResultSet newInstance(ResultSet rs, Log statementLog, int queryStack) {
   InvocationHandler handler = new ResultSetLogger(rs, statementLog, queryStack);
   ClassLoader cl = ResultSet.class.getClassLoader();
   return (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, handler);
 }

6.2 打印执行结果

public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
   try {
     if (Object.class.equals(method.getDeclaringClass())) {
       return method.invoke(this, params);
     }    
     Object o = method.invoke(rs, params);
     if (“next”.equals(method.getName())) {
       if (((Boolean) o)) {
         rows++;
         if (isTraceEnabled()) {
           ResultSetMetaData rsmd = rs.getMetaData();
           final int columnCount = rsmd.getColumnCount();
           if (first) {
             first = false;
             printColumnHeaders(rsmd, columnCount);
           }
           printColumnValues(columnCount);
         }
       } else {
         debug("     Total: " + rows, false);
       }
     }
     clearColumnInfo();
     return o;
   } catch (Throwable t) {
     throw ExceptionUtil.unwrapThrowable(t);
   }
 }

6.3 打印列头

private void printColumnHeaders(ResultSetMetaData rsmd, int columnCount) throws SQLException {
   StringBuilder row = new StringBuilder();
   row.append("   Columns: “);
   for (int i = 1; i <= columnCount; i++) {
     if (BLOB_TYPES.contains(rsmd.getColumnType(i))) {
       blobColumns.add(i);
     }
     String colname = rsmd.getColumnLabel(i);
     row.append(colname);
     if (i != columnCount) {
       row.append(”, ");
     }
   }
   trace(row.toString(), false);
 }

6.4 打印列值

private void printColumnValues(int columnCount) {
   StringBuilder row = new StringBuilder();
   row.append("       Row: “);
   for (int i = 1; i <= columnCount; i++) {
     String colname;
     try {
       if (blobColumns.contains(i)) {
         colname = “<>”;
       } else {
         colname = rs.getString(i);
       }
     } catch (SQLException e) {
       // generally can’t call getString() on a BLOB column
       colname = “<>”;
     }
     row.append(colname);
     if (i != columnCount) {
       row.append(”, ");
     }
   }
   trace(row.toString(), false);
 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis是一个优秀的持久层框架,可以将数据库的操作变得非常简单和高效。在使用MyBatis执行SQL语句时,我们可以通过配置来开启或关闭MyBatis日志记录功能,从而实现原样输出SQL执行语句。 要想在控制台输出MyBatisSQL执行语句,我们可以通过在配置文件中配置`logImpl`属性为"log4j"、"java.util.logging"或"SLF4J"等日志实现类的全限定名,来实现日志输出。 例如,如果我们使用log4j作为日志记录的实现,我们需要在配置文件中添加如下配置: ``` <configuration> <properties> <!-- 其他配置 --> <property name="mybatis.log.impl" value="org.apache.ibatis.logging.log4j.Log4jImpl"/> </properties> <!-- 其他配置 --> </configuration> ``` 配置完成后,MyBatis将使用log4j来进行日志记录,并将SQL执行语句原样输出到控制台或日志文件中,方便我们进行调试和优化。 当我们执行具体的SQL语句时,MyBatis会在日志输出类似下面的信息: ``` DEBUG [main] statement - ==> Preparing: SELECT * FROM user WHERE id = ? DEBUG [main] statement - ==> Parameters: 1(Integer) ``` 可以看到,MyBatis首先会输出"Preparing",表示正在准备执行SQL语句;然后会输出"Parameters",表示SQL语句中的参数值。 通过这种方式,我们可以清晰地看到MyBatis执行SQL语句及参数值,方便我们进行调试和排查问题。同时,MyBatis还提供了其他一些配置项,可以进一步调整日志输出方式和级别,以满足我们的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值