东华大学考研复试机试OJ刷题攻略 📅 发布时间:2026/8/26 4:25:45 👁 浏览次数: 1. 项目背景与核心价值作为一名计算机专业考研过来人我深知东华大学复试机试环节的重要性。OJOnline Judge在线编程平台是检验考生算法能力和编码水平的关键战场而每日3题打卡正是我当年备战期间总结出的高效训练方法。这个系列记录了我从第43天到第45天的刷题历程包含完整的解题思路、代码实现和易错点分析。这套方法的核心价值在于系统性每日固定题量形成持续训练节奏针对性题目选自东华历年复试真题库实战性所有代码均在OJ平台实测通过可复用解题模板可直接用于考场实战2. 每日训练方法论2.1 题目筛选策略我采用的选题三三制原则难度梯度1基础1进阶1挑战题比例可动态调整题型覆盖必含字符串处理、动态规划等高频考点时间控制总时长控制在2小时内模拟真实考场重要提示第43天开始应侧重图论算法这是东华近年考核重点2.2 标准解题流程每道题严格遵循以下步骤问题重述用自己语言描述题目要求输入输出分析明确边界条件算法设计手写伪代码验证思路编码实现使用C11标准编写测试用例设计5类测试数据复杂度分析计算时间/空间复杂度3. 第43天真题详解3.1 矩阵连通区域计数DFS应用// 典型岛屿问题变种 void dfs(vectorvectorint grid, int i, int j) { if(i0 || j0 || igrid.size() || jgrid[0].size() || grid[i][j]!1) return; grid[i][j] 0; // 标记已访问 dfs(grid,i1,j); dfs(grid,i-1,j); dfs(grid,i,j1); dfs(grid,i,j-1); }常见错误忘记处理对角线连通情况题目明确要求访问标记与输入数据冲突建议使用独立标记数组递归层数过多导致栈溢出矩阵过大时改用BFS3.2 字符串模式匹配KMP优化vectorint buildNext(const string pattern) { vectorint next(pattern.length()); next[0] -1; int j -1; for(int i1; ipattern.length(); i) { while(j0 pattern[i]!pattern[j1]) j next[j]; if(pattern[i] pattern[j1]) j; next[i] j; } return next; }性能对比方法时间复杂度适用场景暴力匹配O(mn)短文本KMPO(mn)含大量重复模式Boyer-MooreO(n/m)字符集较大时3.3 背包问题变种动态规划题目要求在传统01背包基础上增加必选物品约束状态转移方程优化dp[i][j] max( dp[i-1][j], dp[i-1][j-w[i]] v[i] // 常规选择 ) if 物品i是必选的: dp[i][j] dp[i-1][j-w[i]] v[i] // 强制选择4. 第44天难点突破4.1 拓扑排序应用课程安排问题关键点在于检测环的存在bool canFinish(int numCourses, vectorvectorint prerequisites) { vectorvectorint graph(numCourses); vectorint inDegree(numCourses,0); for(auto p : prerequisites){ graph[p[1]].push_back(p[0]); inDegree[p[0]]; } queueint q; for(int i0; inumCourses; i) if(inDegree[i]0) q.push(i); int count 0; while(!q.empty()){ int cur q.front(); q.pop(); count; for(int neighbor : graph[cur]){ if(--inDegree[neighbor] 0) q.push(neighbor); } } return count numCourses; }4.2 线段树实现区间查询动态开点线段树模板struct Node { int l, r; int sum; Node *left, *right; Node(int l, int r): l(l), r(r), sum(0), left(nullptr), right(nullptr) {} }; void update(Node* root, int index, int val) { if(root-l root-r) { root-sum val; return; } int mid (root-l root-r) 1; if(index mid) { if(!root-left) root-left new Node(root-l, mid); update(root-left, index, val); } else { if(!root-right) root-right new Node(mid1, root-r); update(root-right, index, val); } root-sum (root-left ? root-left-sum : 0) (root-right ? root-right-sum : 0); }5. 第45天综合训练5.1 多源最短路径Floyd-Warshallvoid floyd(vectorvectorint dist) { int n dist.size(); for(int k0; kn; k) for(int i0; in; i) for(int j0; jn; j) if(dist[i][k]!INT_MAX dist[k][j]!INT_MAX) dist[i][j] min(dist[i][j], dist[i][k]dist[k][j]); }5.2 并查集优化路径压缩按秩合并vectorint parent; vectorint rank; int find(int x) { return parent[x] x ? x : parent[x] find(parent[x]); } void unionSet(int x, int y) { x find(x); y find(y); if(x y) return; if(rank[x] rank[y]) swap(x,y); parent[y] x; if(rank[x] rank[y]) rank[x]; }5.3 单调栈应用柱状图最大矩形# 1. 概述本文我们来分享 MyBatis 的日志模块对应logging包。如下图所示logging包在 《精尽 MyBatis 源码解析 —— 项目结构一览》 中简单介绍了这个模块如下无论在开发测试环境中还是在线上生产环境中日志在整个系统中的地位都是非常重要的。良好的日志功能可以帮助开发人员和测试人员快速定位 Bug 代码也可以帮助运维人员快速定位性能瓶颈等问题。目前的 Java 世界中存在很多优秀的日志框架例如 Log4j、 Log4j2、Slf4j 等。MyBatis 作为一个设计优良的框架除了提供详细的日志输出信息还要能够集成多种日志框架其日志模块的一个主要功能就是集成第三方日志框架。本文涉及的类如下图所示从图的上面部分我们可以看到非常多的 Logger 类的实现分别对应我们常用的日志框架 Log4j、Slf4j 这就是 MyBatis 对这些日志框架的适配。从图的下面部分我们可以看到 BaseJdbcLogger 以及它的四个子类这个不是将日志框架适配到 MyBatis 而是 MyBatis 通过 JDK 动态代理的方式将 JDBC 的操作打印到日志中。即 《设置Mybatis打印调试sql的两种方式》 。下面我们分成三个部分来分享第一个部分MyBatis 的日志模块对多种日志框架的适配。第二个部分MyBatis 如何将 JDBC 的操作打印到日志中。第三个部分收尾看看 Log 接口和 LogFactory 工厂。2. 适配多种日志框架在logging包下我们可以看到有一系列 Logger 的实现类如下图所示Logger 实现类实际上MyBatis 不仅仅提供了上图这些 Logger 的实现类还有例如commons-logging、log4j2、stdout、slf4j等等。完整的胖友可以看看logging包 。那么MyBatis 是如何选择使用哪种 Logger 的实现类的呢或者说如何适配到项目中已经使用的日志框架呢答案就在LogFactory中。代码如下// LogFactory.java /** * 日志构造器 * * 通过构造方法创建对应的 Log 对象 */ private static Constructor? extends Log logConstructor; static { // 1 逐个尝试判断使用哪个日志框架 // 顺序为slf4j → commonsLoging → log4j2 → log4j → jdkLogging tryImplementation(LogFactory::useSlf4jLogging); tryImplementation(LogFactory::useCommonsLogging); tryImplementation(LogFactory::useLog4J2Logging); tryImplementation(LogFactory::useLog4JLogging); tryImplementation(LogFactory::useJdkLogging); // 2 都找不到使用 NoLogging tryImplementation(LogFactory::useNoLogging); } private static void tryImplementation(Runnable runnable) { if (logConstructor null) { try { runnable.run(); } catch (Throwable t) { // ignore } } }1处按照slf4j → commonsLoging → log4j2 → log4j → jdkLogging的顺序逐个尝试判断使用哪个日志框架。因为不同的项目中会使用不同的日志框架而 MyBatis 也无法限制用户使用哪个日志框架所以只好通过这样的方式适配。2处如果都找不到使用 NoLogging 实现类即不打印日志。以#useSlf4jLogging()方法为例子代码如下// LogFactory.java public static synchronized void useSlf4jLogging() { setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class); } private static void setImplementation(Class? extends Log implClass) { try { // 获得参数为 String 的构造方法 Constructor? extends Log candidate implClass.getConstructor(String.class); // 创建 Log 对象 Log log candidate.newInstance(LogFactory.class.getName()); if (log.isDebugEnabled()) { log.debug(Logging initialized using implClass adapter.); } // 创建成功意味着可以使用设置为 logConstructor logConstructor candidate; } catch (Throwable t) { throw new LogException(Error setting Log implementation. Cause: t, t); } }通过反射的方式创建对应的 Log 实现类的对象。如果创建成功意味着可以使用设置为logConstructor属性。其它#useCommonsLogging()等方法代码类似胖友自己瞅瞅。另外MyBatis 也提供了#useCustomLogging(Class? extends Log clazz)方法可以使用自定义的 Log 实现类。代码如下// LogFactory.java public static synchronized void useCustomLogging(Class? extends Log clazz) { setImplementation(clazz); }当然一般情况下我们不会自定义 Log 的实现类。3. 打印 JDBC 操作在实际场景下我们连接数据库执行 SQL 都会打印执行的 SQL 语句、参数、结果等等。那么MyBatis 是如何实现该功能的呢答案在BaseJdbcLogger以及它的四个子类中。3.1 BaseJdbcLoggerorg.apache.ibatis.logging.jdbc.BaseJdbcLogger实现 Log 接口负责打印 JDBC 操作日志的抽象基类。属性如下// BaseJdbcLogger.java /** * 支持的 Log 的构造方法参数的类型 */ protected static final SetString SET_METHODS; /** * 支持的 Log 的执行方法的类型 */ protected static final SetString EXECUTE_METHODS new HashSet(); /** * {link #SET_METHODS} 的初始化 */ static { SET_METHODS new HashSet(); SET_METHODS.add(setString); SET_METHODS.add(setInt); SET_METHODS.add(setBoolean); SET_METHODS.add(setShort); SET_METHODS.add(setLong); SET_METHODS.add(setDouble); SET_METHODS.add(setFloat); SET_METHODS.add(setTimestamp); SET_METHODS.add(setDate); SET_METHODS.add(setTime); SET_METHODS.add(setArray); SET_METHODS.add(setBigDecimal); SET_METHODS.add(setAsciiStream); SET_METHODS.add(setBinaryStream); SET_METHODS.add(setBlob); SET_METHODS.add(setObject); SET_METHODS.add(setCharacterStream); SET_METHODS.add(setNCharacterStream); SET_METHODS.add(setURL); SET_METHODS.add(setNull); } /** * {link #EXECUTE_METHODS} 的初始化 */ static { EXECUTE_METHODS.add(execute); EXECUTE_METHODS.add(executeUpdate); EXECUTE_METHODS.add(executeQuery); EXECUTE_METHODS.add(addBatch); } /** * Log 对象 */ protected final Log statementLog; /** * 查询的层级 */ protected final int queryStack; /** * 参数集合 * * KEY参数的位置 * VALUE参数的值 */ private final MapInteger, String columnMap new HashMap(); /** * 参数集合 */ private final ListObject columnValues new ArrayList();SET_METHODS静态属性支持的 Log 的构造方法参数的类型。即PreparedStatement 类的方法的集合。EXECUTE_METHODS静态属性支持的 Log 的执行方法的类型。即Statement 和 PreparedStatement 类的方法的集合。statementLog属性Log 对象。queryStack属性查询的层级。考虑到可能多个方法嵌套调用。columnMap属性参数集合。KEY参数的位置VALUE参数的值。columnValues属性参数集合。3.1.1 构造方法// BaseJdbcLogger.java public BaseJdbcLogger(Log log, int queryStack) { this.statementLog log; if (queryStack 0) { this.queryStack 1; } else { this.queryStack queryStack; } }3.1.2 setColumn#setColumn(String key, Object value)方法设置参数。代码如下// BaseJdbcLogger.java protected void setColumn(String key, Object value) { columnMap.put(columnValues.size(), key); columnValues.add(value); }3.1.3 getParameterValueString#getParameterValueString()方法获得参数的打印值。代码如下// BaseJdbcLogger.java protected String getParameterValueString() { ListObject typeList new ArrayList(columnValues.size()); // 遍历 columnValues 数组 for (Object value : columnValues) { if (value null) { typeList.add(null); } else { // 转换成字符串 typeList.add(objectValueString(value) ( value.getClass().getSimpleName() )); } } // 拼接字符串 final String parameters typeList.toString(); return parameters.substring(1, parameters.length() - 1); }3.1.4 objectValueString#objectValueString(Object value)方法将参数转换成字符串。代码如下// BaseJdbcLogger.java protected String objectValueString(Object value) { if (value instanceof Array) { try { return ArrayUtil.toString(((Array) value).getArray()); } catch (SQLException e) { return value.toString(); } } return value.toString(); }3.1.5 getColumnString#getColumnString()方法获得参数的名称。代码如下// BaseJdbcLogger.java protected String getColumnString() { return columnMap.toString(); }3.1.6 clearColumn#clearColumn()方法清空参数。代码如下// BaseJdbcLogger.java protected void clearColumn() { columnMap.clear(); columnValues.clear(); }3.1.7 isDebugEnabled#isDebugEnabled()方法判断是否 debug 开启。代码如下// BaseJdbcLogger.java Override public boolean isDebugEnabled() { return statementLog.isDebugEnabled(); }3.1.8 isTraceEnabled#isTraceEnabled()方法判断是否 trace 开启。代码如下// BaseJdbcLogger.java Override public boolean isTraceEnabled() { return statementLog.isTraceEnabled(); }3.1.9 debug#debug(String text)方法打印 debug 日志。代码如下// BaseJdbcLogger.java Override public void debug(String text) { statementLog.debug(text); }3.1.10 trace#trace(String text)方法打印 trace 日志。代码如下// BaseJdbcLogger.java Override public void trace(String text) { statementLog.trace(text); }3.1.11 error#error(String text, Throwable throwable)方法打印 error 日志。代码如下// BaseJdbcLogger.java Override public void error(String text, Throwable throwable) { statementLog.error(text, throwable); }3.2 ConnectionLoggerorg.apache.ibatis.logging.jdbc.ConnectionLogger继承 BaseJdbcLogger 抽象类Connection 的日志实现类。3.2.1 构造方法// ConnectionLogger.java /** * Connection 对象的代理 */ private final Connection connection; private ConnectionLogger(Connection conn, Log statementLog, int queryStack) { super(statementLog, queryStack); this.connection conn; }3.2.2 newInstance#newInstance(Connection connection, Log statementLog, int queryStack)静态方法创建 Connection 的代理对象。代码如下// ConnectionLogger.java public static Connection newInstance(Connection conn, Log statementLog, int queryStack) { // 创建 InvocationHandler 对象 InvocationHandler handler new ConnectionLogger(conn, statementLog, queryStack); // 创建 ClassLoader 对象 ClassLoader cl Connection.class.getClassLoader(); // 创建代理对象 return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler); }3.2.3 invoke// ConnectionLogger.java Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { try { // 如果调用的是 Object 的方法直接调用不进行代理 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, params); } // 如果调用的是 prepareStatement 方法进行日志处理 if (prepareStatement.equals(method.getName())) { if (isDebugEnabled()) { debug( Preparing: removeBreakingWhitespace((String) params[0]), true); } // 执行方法 PreparedStatement stmt (PreparedStatement) method.invoke(connection, params); // 创建 PreparedStatement 的代理对象 stmt PreparedStatementLogger.newInstance(stmt, statementLog, queryStack); return stmt; // 如果调用的是 prepareCall 方法进行日志处理 } else if (prepareCall.equals(method.getName())) { if (isDebugEnabled()) { debug( Preparing: removeBreakingWhitespace((String) params[0]), true); } // 执行方法 PreparedStatement stmt (PreparedStatement) method.invoke(connection, params); // 创建 PreparedStatement 的代理对象 stmt PreparedStatementLogger.newInstance(stmt, statementLog, queryStack); return stmt; // 如果调用的是 createStatement 方法进行日志处理 } else if (createStatement.equals(method.getName())) { // 执行方法 Statement stmt (Statement) method.invoke(connection, params); // 创建 Statement 的代理对象 stmt StatementLogger.newInstance(stmt, statementLog, queryStack); return stmt; } else { return method.invoke(connection, params); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }根据不同的方法进行不同的日志处理。例如#prepareStatement(String sql)方法会打印要执行的 SQL 语句并创建 PreparedStatement 的代理对象。#prepareCall(String sql)方法会打印要执行的 SQL 语句并创建 PreparedStatement 的代理对象。#createStatement()方法会创建 Statement 的代理对象。通过这样的方式让 Connection 的所有操作都打印相应的日志。3.2.4 getConnection#getConnection()方法获得真正的 Connection 对象。代码如下// ConnectionLogger.java public Connection getConnection() { return connection; }3.3 PreparedStatementLoggerorg.apache.ibatis.logging.jdbc.PreparedStatementLogger继承 BaseJdbcLogger 抽象类PreparedStatement 的日志实现类。3.3.1 构造方法// PreparedStatementLogger.java /** * PreparedStatement 对象的代理 */ private final PreparedStatement statement; private PreparedStatementLogger(PreparedStatement stmt, Log statementLog, int queryStack) { super(statementLog, queryStack); this.statement stmt; }3.3.2 newInstance#newInstance(PreparedStatement stmt, Log statementLog, int queryStack)静态方法创建 PreparedStatement 的代理对象。代码如下// PreparedStatementLogger.java public static PreparedStatement newInstance(PreparedStatement stmt, Log statementLog, int queryStack) { // 创建 InvocationHandler 对象 InvocationHandler handler new PreparedStatementLogger(stmt, statementLog, queryStack); // 创建 ClassLoader 对象 ClassLoader cl PreparedStatement.class.getClassLoader(); // 创建代理对象 return (PreparedStatement) Proxy.newProxyInstance(cl, new Class[]{PreparedStatement.class, CallableStatement.class}, handler); }3.3.3 invoke// PreparedStatementLogger.java Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { try { // 如果调用的是 Object 的方法直接调用不进行代理 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, params); } // 执行相应的设置参数的方法 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); // 如果是执行 SQL 的方法 } else if (EXECUTE_METHODS.contains(method.getName())) { if (isDebugEnabled()) { debug(Parameters: getParameterValueString(), true); } // 清空参数 clearColumn(); // 执行方法 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 { return method.invoke(statement, params); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }根据不同的方法进行不同的日志处理。例如调用#setXXX(int parameterIndex, ...)等等方法会调用#setColumn(String key, Object value)方法记录参数。调用#executeXXX()等等方法会调用#getParameterValueString()方法打印参数并创建 ResultSet 的代理对象。通过这样的方式让 PreparedStatement 的所有操作都打印相应的日志。3.3.4 getStatement#getStatement()方法获得真正的 PreparedStatement 对象。代码如下// PreparedStatementLogger.java public PreparedStatement getStatement() { return statement; }3.4 StatementLoggerorg.apache.ibatis.logging.jdbc.StatementLogger继承 BaseJdbcLogger 抽象类Statement 的日志实现类。3.4.1 构造方法// StatementLogger.java /** * Statement 对象的代理 */ private final Statement statement; private StatementLogger(Statement stmt, Log statementLog, int queryStack) { super(statementLog, queryStack); this.statement stmt; }3.4.2 newInstance#newInstance(Statement stmt, Log statementLog, int queryStack)静态方法创建 Statement 的代理对象。代码如下// StatementLogger.java public static Statement newInstance(Statement stmt, Log statementLog, int queryStack) { // 创建 InvocationHandler 对象 InvocationHandler handler new StatementLogger(stmt, statementLog, queryStack); // 创建 ClassLoader 对象 ClassLoader cl Statement.class.getClassLoader(); // 创建代理对象 return (Statement) Proxy.newProxyInstance(cl, new Class[]{Statement.class}, handler); }3.4.3 invoke// StatementLogger.java Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { try { // 如果调用的是 Object 的方法直接调用不进行代理 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, params); } // 如果是执行 SQL 的方法 if (EXECUTE_METHODS.contains(method.getName())) { if (isDebugEnabled()) { debug(Parameters: getParameterValueString(), 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); } // 如果是设置 SQL 的方法 } else if (getResultSet.equals(method.getName())) { // 执行方法 ResultSet rs (ResultSet) method.invoke(statement, params); return rs null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack); // 如果是设置 SQL 的方法 } 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); } }根据不同的方法进行不同的日志处理。例如调用#executeXXX()等等方法会调用#getParameterValueString()方法打印参数并创建 ResultSet 的代理对象。调用#getResultSet()方法会创建 ResultSet 的代理对象。调用#getUpdateCount()方法会打印更新的数量。通过这样的方式让 Statement 的所有操作都打印相应的日志。3.4.4 getStatement#getStatement()方法获得真正的 Statement 对象。代码如下// StatementLogger.java public Statement getStatement() { return statement; }3.5 ResultSetLoggerorg.apache.ibatis.logging.jdbc.ResultSetLogger继承 BaseJdbcLogger 抽象类ResultSet 的日志实现类。3.5.1 构造方法// ResultSetLogger.java /** * ResultSet 对象的代理 */ private final ResultSet rs; /** * 第一列的位置 */ private final int firstColumn; /** * 列的数量 */ private int columnCount; /** * 列名数组 */ private String[] columnNames; /** * 列的类型数组 */ private String[] columnClassName; /** * 统计读取的列的数量 */ private final SetInteger blobColumns new HashSet(); private ResultSetLogger(ResultSet rs, Log statementLog, int queryStack) { super(statementLog, queryStack); this.rs rs; // 第一列的位置 this.firstColumn (rs instanceof CallableStatement) ? FIRST_COLUMN_INDEX_CALLABLE_STATEMENT : FIRST_COLUMN_INDEX_NORMAL_STATEMENT; }firstColumn属性第一列的位置。因为CallableStatement和PreparedStatement的位置不同。3.5.2 newInstance#newInstance(ResultSet rs, Log statementLog, int queryStack)静态方法创建 ResultSet 的代理对象。代码如下// ResultSetLogger.java public static ResultSet newInstance(ResultSet rs, Log statementLog, int queryStack) { // 创建 InvocationHandler 对象 InvocationHandler handler new ResultSetLogger(rs, statementLog, queryStack); // 创建 ClassLoader 对象 ClassLoader cl ResultSet.class.getClassLoader(); // 创建代理对象 return (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, handler); }3.5.3 invoke// ResultSetLogger.java Override public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { try { // 如果调用的是 Object 的方法直接调用不进行代理 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, params); } // 执行方法 Object o method.invoke(rs, params); // 如果是 next 方法 if (next.equals(method.getName())) { // 返回 true 说明有记录 if ((Boolean) o) { // 增加行数 rows; // 处于 debug 模式则打印日志 if (isTraceEnabled()) { ResultSetMetaData rsmd rs.getMetaData(); // 初始化 columnCount if (columnCount 0) { columnCount rsmd.getColumnCount(); } // 打印列名 printColumnHeaders(rsmd, columnCount); // 打印列值 printColumnValues(columnCount); } } else { // 打印总行数 debug( Total: rows, false); } } // 清空 columnMap 和 columnValues clearColumn(); return o; } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }根据不同的方法进行不同的日志处理。例如调用#next()方法会打印每条记录的所有列的值。通过这样的方式让 ResultSet 的所有操作都打印相应的日志。3.5.4 printColumnHeaders#printColumnHeaders(ResultSetMetaData rsmd, int columnCount)方法打印列名。代码如下// ResultSetLogger.java private void printColumnHeaders(ResultSetMetaData rsmd, int columnCount) throws SQLException { // 初始化 columnNames 和 columnClassName 属性 if (columnNames null) { columnNames new String[columnCount 1]; columnClassName new String[columnCount 1]; for (int i 1; i columnCount; i) { columnNames[i] rsmd.getColumnLabel(i); columnClassName[i] rsmd.getColumnClassName(i); } } // 拼接日志 if (columnCount 1) { trace( Columns: columnNames[1]); } else if (columnCount 1) { StringBuilder sb new StringBuilder( Columns: ); sb.append(columnNames[1]); for (int i 2; i columnCount; i) { sb.append(, ); sb.append(columnNames[i]); } trace(sb.toString()); } }3.5.5 printColumnValues#printColumnValues(int columnCount)方法打印列值。代码如下// ResultSetLogger.java private void printColumnValues(int columnCount) { if (columnCount 1) { trace( Row: getColumnValue(1)); } else if (columnCount 1) { StringBuilder sb new StringBuilder( Row: ); sb.append(getColumnValue(1)); for (int i 2; i columnCount; i) { sb.append(, ); sb.append(getColumnValue(i)); } trace(sb.toString()); } }3.5.6 getColumnValue#getColumnValue(int column)方法获得指定列的值。代码如下// ResultSetLogger.java private String getColumnValue(int column) { try { // 判断是否为 BLOB 类型 if (bl