1. 配电网重构与最优流法概述
配电网重构是电力系统运行中的一项关键技术,它通过改变网络拓扑结构来优化系统运行状态。我在电力系统自动化领域工作多年,见证了这项技术从理论研究到工程应用的完整发展历程。最优流法(Optimal Power Flow, OPF)作为其中的核心算法,已经成为现代智能配电网不可或缺的优化工具。
传统配电网运行中,我们常遇到三个典型问题:网损过高导致运行成本增加、负荷分配不均引发设备过载、电压质量不达标影响用户用电。2018年参与# 1. 概述
本文,我们来分享 MyBatis 的日志模块,对应 logging 包。如下图所示:
logging 包
在 《精尽 MyBatis 源码解析 —— 项目结构一览》 中,简单介绍了这个模块如下:
无论在开发测试环境中,还是在线上生产环境中,日志在整个系统中的地位都是非常重要的。良好的日志功能可以帮助开发人员和测试人员快速定位 Bug 代码,也可以帮助运维人员快速定位性能瓶颈等问题。目前的 Java 世界中存在很多优秀的日志框架,例如 Log4j、 Log4j2、Slf4j 等。
MyBatis 作为一个设计优良的框架,除了提供详细的日志输出信息,还要能够集成多种日志框架,其日志模块的一个主要功能就是集成第三方日志框架。
本文涉及的类如下图所示:
- 从图的上面部分,我们可以看到非常多的 Logger 类的实现,分别对应我们常用的日志框架 Log4j、Slf4j ,这就是 MyBatis 为了集成这些第三方日志框架所做的实现。
- 从图的下面部分,我们可以看到 BaseJdbcLogger 以及它的四个子类,这个是通过动态代理的方式,将 JDBC 的操作,打印成日志。
下面,让我们开始逐节的进行解析。
2. LogFactory
org.apache.ibatis.logging.LogFactory ,Log 工厂类。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2.1 构造方法
java复制// LogFactory.java
/**
* Marker to be used by logging implementations that support markers
*/
public static final String MARKER = "MYBATIS";
/**
* 使用的 Log 的构造方法
*/
private static Constructor<? extends Log> logConstructor;
static {
// <1> 逐个尝试,判断使用哪个 Log 的实现类,即初始化 logConstructor 属性
tryImplementation(LogFactory::useSlf4jLogging);
tryImplementation(LogFactory::useCommonsLogging);
tryImplementation(LogFactory::useLog4J2Logging);
tryImplementation(LogFactory::useLog4JLogging);
tryImplementation(LogFactory::useJdkLogging);
tryImplementation(LogFactory::useNoLogging);
}
-
<1>处,我们可以看到,按照 Slf4j、CommonsLogging、Log4J2、Log4J、JdkLogging、NoLogging 的顺序,逐个尝试,判断使用哪个 Log 的实现类,即初始化logConstructor属性。 -
#tryImplementation(Runnable runnable)方法,尝试调用方法。代码如下:java复制// LogFactory.java private static void tryImplementation(Runnable runnable) { if (logConstructor == null) { try { runnable.run(); } catch (Throwable t) { // ignore } } }- 在
logConstructor为空的情况下,才会执行runnable的方法。
- 在
-
#useXXXLogging()方法,尝试使用对应的 Log 的实现类。代码如下:java复制// LogFactory.java public static synchronized void useSlf4jLogging() { setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class); } public static synchronized void useCommonsLogging() { setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class); } public static synchronized void useLog4JLogging() { setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class); } public static synchronized void useLog4J2Logging() { setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class); } public static synchronized void useJdkLogging() { setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class); } public static synchronized void useStdOutLogging() { setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class); } public static synchronized void useNoLogging() { setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class); }-
通过调用
#setImplementation(Class<? extends Log> implClass)方法,设置使用的logConstructor属性。代码如下:java复制// LogFactory.java 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); } }- 通过反射的方式,获得参数为 String 的构造方法,后创建 Log 对象。如果创建成功,意味着可以使用,设置为
logConstructor属性。
- 通过反射的方式,获得参数为 String 的构造方法,后创建 Log 对象。如果创建成功,意味着可以使用,设置为
-
2.2 getLog
#getLog(...) 静态方法,获得 Log 对象。代码如下:
java复制// LogFactory.java
public static Log getLog(Class<?> aClass) {
return getLog(aClass.getName());
}
public static Log getLog(String logger) {
try {
return logConstructor.newInstance(logger);
} catch (Throwable t) {
throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t);
}
}
2.3 useCustomLogging
#useCustomLogging(Class<? extends Log> clazz) 方法,使用自定义的 Log 实现类。代码如下:
java复制// LogFactory.java
public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
setImplementation(clazz);
}
3. Log
org.apache.ibatis.logging.Log ,MyBatis Log 接口。代码如下:
java复制// Log.java
public interface Log {
boolean isDebugEnabled();
boolean isTraceEnabled();
void error(String s, Throwable e);
void error(String s);
void debug(String s);
void trace(String s);
void warn(String s);
}
- 具体的 Log 实现类,可以看看
logging包下的实现类。
4. BaseJdbcLogger
在 JDBC 中,核心的类有:
java.sql.Connection对象,表示连接。java.sql.Statement对象,用于操作数据库。java.sql.PreparedStatement对象,用于操作数据库,是 Statement 的子类。java.sql.ResultSet对象,表示返回的结果集。
那么,org.apache.ibatis.logging.BaseJdbcLogger ,就是通过对这些核心类的方法进行拦截,从而实现打印日志的功能。所以,BaseJdbcLogger 内部会定义这些核心类的常量。代码如下:
java复制// BaseJdbcLogger.java
/**
* Connection 接口的方法定义
*/
protected static final Set<String> SET_METHODS;
/**
* Statement 接口的方法定义
*/
protected static final Set<String> EXECUTE_METHODS = new HashSet<>();
static {
SET_METHODS = new HashSet<>();
// Connection 接口
SET_METHODS.add("setAutoCommit");
SET_METHODS.add("setReadOnly");
SET_METHODS.add("setTransactionIsolation");
SET_METHODS.add("setCatalog");
SET_METHODS.add("setTypeMap");
// Statement 接口
EXECUTE_METHODS.add("execute");
EXECUTE_METHODS.add("executeUpdate");
EXECUTE_METHODS.add("executeQuery");
EXECUTE_METHODS.add("addBatch");
EXECUTE_METHODS.add("executeBatch");
}
- 通过静态代码块,初始化了
SET_METHODS和EXECUTE_METHODS属性。
BaseJdbcLogger 是一个抽象类,它的子类有四个:
- ConnectionLogger
- PreparedStatementLogger
- StatementLogger
- ResultSetLogger
那么,BaseJdbcLogger 是如何实现拦截这些核心类的方法的呢?答案是通过动态代理的方式。而实际上,动态代理分为接口和类的代理:
- 基于接口的代理,使用 JDK 提供的
java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy来实现。 - 基于类的代理,使用 CGLIB 来实现。
那么,BaseJdbcLogger 使用的是什么方式呢?因为 Connection、Statement、PreparedStatement、ResultSet 都是接口,所以使用的是 JDK 提供的接口代理的方式。所以,BaseJdbcLogger 实现了 java.lang.reflect.InvocationHandler 接口。代码如下:
java复制// BaseJdbcLogger.java
public abstract class BaseJdbcLogger implements InvocationHandler {
}
4.1 构造方法
java复制// BaseJdbcLogger.java
/**
* 日志的 Log 对象
*/
protected final Log statementLog;
/**
* 查询的层级,用于格式化输出
*/
protected final int queryStack;
/**
* Creates a new BaseJdbcLogger.
*
* @param log the log
* @param queryStack the query stack
*/
public BaseJdbcLogger(Log log, int queryStack) {
this.statementLog = log;
if (queryStack == 0) {
this.queryStack = 1;
} else {
this.queryStack = queryStack;
}
}
statementLog属性,Log 对象。打印日志时,使用到该对象。queryStack属性,查询的层级。考虑到,可能 Statement 会嵌套创建 Statement 的情况,例如Statement#executeQuery(String sql)方法,会创建 ResultSet 对象。
4.2 invoke
#invoke(Object proxy, Method method, Object[] params) 方法,代理方法。代码如下:
java复制// BaseJdbcLogger.java
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
// 获得方法来源的类
Object o = method.getDeclaringClass();
if (Object.class.equals(o)) { // <1>
return method.invoke(this, params);
}
// 空方法,直接调用
if (o.isInterface()) { // <2>
return method.invoke(this, params);
}
// <3>
Object result = method.invoke(realObject, params);
// 如果是 SET_METHODS 的方法,则打印参数日志
if (SET_METHODS.contains(method.getName())) {
if ("setCatalog".equals(method.getName())) {
setColumn("databasename", params[0]);
}
// 如果是 EXECUTE_METHODS 的方法,则打印日志
} else if (EXECUTE_METHODS.contains(method.getName())) {
if ("executeQuery".equals(method.getName())) {
// 打印日志
if (result instanceof ResultSet) {
ResultSet rs = (ResultSet) result;
return ResultSetLogger.newInstance(rs, statementLog, queryStack); // <4>
} else {
return result;
}
} else {
// 打印日志
prepareDebugString(proxy, method, params);
}
} else if ("getResultSet".equals(method.getName())) {
// 打印日志
if (result instanceof ResultSet) {
ResultSet rs = (ResultSet) result;
return ResultSetLogger.newInstance(rs, statementLog, queryStack); // <4>
} else {
return result;
}
} else if ("getUpdateCount".equals(method.getName())) {
// 打印日志
if (result instanceof Integer && (Integer) result != -1) {
prepareDebugString(proxy, method, params);
}
return result;
}
return result;
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
<1>处,如果是 Object 定义的方法,例如#toString()方法,则调用当前对象对应的方法。即,不会进行代理。<2>处,如果是接口的方法,则调用当前对象对应的方法。即,不会进行代理。<3>处,调用被代理对象的方法。- 如果是
SET_METHODS的方法,则打印参数日志。 - 如果是
EXECUTE_METHODS的方法,则打印日志。其中,如果创建了 ResultSet 对象,则进行打印日志,并且创建 ResultSet 的代理对象。 - 如果是
#getResultSet()方法,则打印日志,并且创建 ResultSet 的代理对象。 - 如果是
#getUpdateCount()方法,则打印日志。
- 如果是
<4>处,调用ResultSetLogger#newInstance(ResultSet rs, Log statementLog, int queryStack)方法,创建 ResultSet 的代理对象。详细解析,见 「4.6 ResultSetLogger」 。
4.3 其它方法
java复制// BaseJdbcLogger.java
protected abstract Object getParameterValue(Object parameter);
protected void setColumn(String key, Object value) {
try {
Field field = getClass().getDeclaredField(key);
field.setAccessible(true);
field.set(this, value);
} catch (Exception e) {
// Ignore.
}
}
protected void prepareDebugString(Object proxy, Method method, Object[] params) {
// 忽略
}
protected String paramValueToString(Object value) {
if (value == null) {
return "null";
}
// 基础类型,直接 toString
if (value instanceof Array) {
try {
return ArrayUtil.toString(((Array) value).getArray());
} catch (SQLException e) {
// This can't happen. The Array is from JDBC and not some random method call.
return value.toString();
}
}
// 特殊处理
return value.toString();
}
protected void debug(String text, boolean input) {
if (statementLog.isDebugEnabled()) {
statementLog.debug(prefix(input) + text);
}
}
protected void trace(String text, boolean input) {
if (statementLog.isTraceEnabled()) {
statementLog.trace(prefix(input) + text);
}
}
private String prefix(boolean isInput) {
char[] buffer = new char[queryStack * 2 + 2];
Arrays.fill(buffer, '=');
buffer[queryStack * 2 + 1] = ' ';
return new String(buffer);
}
4.4 ConnectionLogger
org.apache.ibatis.logging.jdbc.ConnectionLogger ,继承 BaseJdbcLogger 类,Connection 的代理。
4.4.1 构造方法
java复制// ConnectionLogger.java
/**
* Connection 对象
*/
private final Connection connection;
private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.connection = conn;
}
4.4.2 newInstance
#newInstance(Connection conn, Log statementLog, int queryStack) 静态方法,创建 Connection 的代理对象。代码如下:
java复制// ConnectionLogger.java
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);
}
4.4.3 invoke
#invoke(Object proxy, Method method, Object[] params) 方法,代理方法。代码如下:
java复制// 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 方法,则打印日志,并创建 PreparedStatement 的代理对象
if ("prepareStatement".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
// 创建 PreparedStatement 对象
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
// 创建 PreparedStatement 的代理对象
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
// 如果调用的是 prepareCall 方法,则打印日志,并创建 CallableStatement 的代理对象
} else if ("prepareCall".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
// 创建 CallableStatement 对象
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
// 创建 CallableStatement 的代理对象
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
// 如果调用的是 createStatement 方法,则创建 Statement 的代理对象
} else if ("createStatement".equals(method.getName())) {
// 创建 Statement 对象
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"、"prepareCall"、"createStatement"方法调用时,会创建对应的代理对象。
4.4.4 getConnection
#getConnection() 方法,获得真正的 Connection 对象。代码如下:
java复制// ConnectionLogger.java
public Connection getConnection() {
return connection;
}
4.5 StatementLogger
org.apache.ibatis.logging.jdbc.StatementLogger ,继承 BaseJdbcLogger 类,Statement 的代理。
4.5.1 构造方法
java复制// StatementLogger.java
/**
* Statement 对象
*/
private final Statement statement;
private StatementLogger(Statement stmt, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.statement = stmt;
}
4.5.2 newInstance
#newInstance(Statement stmt, Log statementLog, int queryStack) 静态方法,创建 Statement 的代理对象。代码如下:
java复制// StatementLogger.java
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.5.3 invoke
#invoke(Object proxy, Method method, Object[] params) 方法,代理方法。代码如下:
java复制// 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);
}
// 如果调用的是 execute 方法
if (EXECUTE_METHODS.contains(method.getName())) {
// 打印日志
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(params), true);
}
// 如果是查询方法
if ("executeQuery".equals(method.getName())) {
// 执行查询方法
ResultSet rs = (ResultSet) method.invoke(statement, params);
// 返回 ResultSet 代理对象
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
// 执行其它方法
return method.invoke(statement, params);
}
// 如果调用的是 getResultSet 方法
} else if ("getResultSet".equals(method.getName())) {
// 执行方法
ResultSet rs = (ResultSet) method.invoke(statement, params);
// 返回 ResultSet 代理对象
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
4.5.4 getStatement
#getStatement() 方法,获得真正的 Statement 对象。代码如下:
java复制// StatementLogger.java
public Statement getStatement() {
return statement;
}
4.6 PreparedStatementLogger
org.apache.ibatis.logging.jdbc.PreparedStatementLogger ,继承 BaseJdbcLogger 类,PreparedStatement 的代理。
4.6.1 构造方法
java复制// PreparedStatementLogger.java
/**
* PreparedStatement 对象
*/
private final PreparedStatement statement;
private PreparedStatementLogger(PreparedStatement stmt, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.statement = stmt;
}
4.6.2 newInstance
#newInstance(PreparedStatement stmt, Log statementLog, int queryStack) 静态方法,创建 PreparedStatement 的代理对象。代码如下:
java复制// PreparedStatementLogger.java
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);
}
4.6.3 invoke
#invoke(Object proxy, Method method, Object[] params) 方法,代理方法。代码如下:
java复制// 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);
}
// 执行方法
Object o = method.invoke(statement, params);
// 如果是 set 方法,打印日志
if (SET_METHODS.contains(method.getName())) {
if ("setNull".equals(method.getName())) {
setColumn(params[0], null);
} else {
setColumn(params[0], params[1]);
}
// 如果是 execute 方法,打印日志
} else if (EXECUTE_METHODS.contains(method.getName())) {
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(), true);
}
// 如果是查询方法
if ("executeQuery".equals(method.getName())) {
ResultSet rs = (ResultSet) o;
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
}
}
return o;
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
4.6.4 getParameterValueString
#getParameterValueString() 方法,获得参数集合的字符串。代码如下:
java复制// PreparedStatementLogger.java
private String getParameterValueString() {
List<Object> typeList = new ArrayList<>();
List<Object> valueList = new ArrayList<>();
// 获得参数
ParameterMetaData pmd;
try {
pmd = statement.getParameterMetaData();
// 获得参数的数量
int paramCount = pmd.getParameterCount();
for (int i = 0; i < paramCount; i++) {
// 添加到 typeList 中
String typeName = pmd.getParameterTypeName(i + 1);
typeList.add(typeName);
// 添加到 valueList 中
Object value;
try {
value = statement.getParameterMetaData().getParameterType(i + 1);
} catch (Throwable e) {
// 一般异常,使用 getObject 获取参数
value = statement.getObject(i + 1);
}
valueList.add(value);
}
} catch (SQLException e) {
return "";
}
// 拼接
return getParameterValueString(typeList, valueList);
}
- 关于
java.sql.ParameterMetaData类,可以看看 《JDBC 中的ParameterMetaData 的使用》 。
#getParameterValueString(List<Object> typeList, List<Object> valueList) 方法,拼接参数集合的字符串。代码如下:
java复制// PreparedStatementLogger.java
private String getParameterValueString(List<Object> typeList, List<Object> valueList) {
StringBuilder output = new StringBuilder();
for (int i = 0, len = valueList.size(); i < len; i++) {
if (i > 0) {
output.append(", ");
}
Object value = valueList.get(i);
if (value == null) {
output.append(typeList.get(i)).append("=null");
} else {
output.append(typeList.get(i)).append("=").append(paramValueToString(value));
}
}
return output.toString();
}
4.6.5 getPreparedStatement
#getPreparedStatement() 方法,获得真正的 PreparedStatement 对象。代码如下:
java复制// PreparedStatementLogger.java
public PreparedStatement getPreparedStatement() {
return statement;
}
4.7 ResultSetLogger
org.apache.ibatis.logging.jdbc.ResultSetLogger ,继承 BaseJdbcLogger 类,ResultSet 的代理。
4.7.1 构造方法
java复制// ResultSetLogger.java
/**
* ResultSet 对象
*/
private final ResultSet rs;
/**
* 统计查询的列数
*/
private final Set<Integer> columnIndices = new HashSet<>();
/**
* 统计映射的列名
*/
private final Set<String> columnNames = new HashSet<>();
/**
* 第一行,可能为 null
*/
private boolean first = true;
/**
* 行数
*/
private int rows;
private ResultSetLogger(ResultSet rs, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.rs = rs;
}
4.7.2 newInstance
#newInstance(ResultSet rs, Log statementLog, int queryStack) 静态方法,创建 ResultSet 的代理对象。代码如下:
java复制// ResultSetLogger.java
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);
}
4.7.3 invoke
#invoke(Object proxy, Method method, Object[] params) 方法,代理方法。代码如下:
java复制// 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())) {
// 遍历下一行
if ((Boolean) o) { // 遍历到下一行
// 增加行数
rows++;
// 如果是 debug 日志级别,并且是第一行,则打印表头
if (isTraceEnabled()) {
ResultSetMetaData rsmd = rs.getMetaData();
final int columnCount = rsmd.getColumnCount();
// 初始化 columnIndices 和 columnNames
if (first) {
first = false;
printColumnHeaders(rsmd, columnCount);
}
// 打印该行的值
printColumnValues(columnCount);
}
} else { // 遍历结束
// 打印总行数
debug(" Total: " + rows, false);
}
}
// 清空 columnIndices 和 columnNames
clearColumnInfo();
return o;
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
- 关于
java.sql.ResultSetMetaData类,可以看看 《JDBC 中的ResultSetMetaData 的使用》 。
#printColumnHeaders(ResultSetMetaData rsmd, int columnCount) 方法,打印表头。代码如下:
java复制// ResultSetLogger.java
private void printColumnHeaders(ResultSetMetaData rsmd, int columnCount) throws SQLException {
StringBuilder row = new StringBuilder();
row.append(" Columns: ");
for (int i = 1; i <= columnCount; i++) {
if (i > 1) {
row.append(", ");
}
// 获得列名
String colname = rsmd.getColumnLabel(i);
if (colname == null) {
colname = rsmd.getColumnName(i);
}
row.append(colname);
// 添加到 columnIndices 和 columnNames 中
columnIndices.add(i);
columnNames.add(colname);
}
trace(row.toString(), false);
}
#printColumnValues(int columnCount) 方法,打印该行的值。代码如下:
java复制// ResultSetLogger.java
private void printColumnValues(int columnCount) {
StringBuilder row = new StringBuilder();
row.append(" Row: ");
for (int i = 1; i <= columnCount; i++) {
if (i > 1) {
row.append(", ");
}
try {
if (columnIndices.contains(i)) {
// 获得值
String colname = columnNames.contains(String.valueOf(i)) ? String.valueOf(i) : rs.getMetaData().getColumnName(i);
Object value = rs.getString(i);
row.append(colname + "=" + value);
}
} catch (SQLException e) {
// generally can't call getString() on a BLOB column
row.append("Cannot read value of column " + i);
}
}
trace(row.toString(), false);
}
#clearColumnInfo() 方法,清空 columnIndices 和 columnNames 。代码如下:
java复制// ResultSetLogger.java
private void clearColumnInfo() {
columnIndices.clear();
columnNames.clear();
}
4.7.4 getResultSet
#getResultSet() 方法,获得真正的 ResultSet 对象。代码如下:
java复制// ResultSetLogger.java
public ResultSet getResultSet() {
return rs;
}
5. JdbcTransaction
org.apache.ibatis.executor.transaction.jdbc.JdbcTransaction ,实现 Transaction 接口,基于 JDBC 的事务实现类。
5.1 构造方法
java复制// JdbcTransaction.java
/**
* Connection 对象
*/
protected Connection connection;
/**
* DataSource 对象
*/
protected DataSource dataSource;
/**
* 事务隔离级别
*/
protected TransactionIsolationLevel level;
/**
* 自动提交
*/
protected boolean autoCommit;
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommit = desiredAutoCommit;
}
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
5.2 getConnection
#getConnection() 方法,获得 Connection 对象。代码如下:
java复制// JdbcTransaction.java
@Override
public Connection getConnection() throws SQLException {
// 如果没连接,创建连接
if (connection == null) {
openConnection();
}
return connection;
}
-
调用
#openConnection()方法,创建 Connection 对象。代码如下:java复制// JdbcTransaction.java protected void openConnection() throws SQLException { if (log.isDebugEnabled()) { log.debug("Opening JDBC Connection"); } // 获得连接 connection = dataSource.getConnection(); // 设置隔离级别 if (level != null) { connection.setTransactionIsolation(level.getLevel()); } // 设置自动提交 setDesiredAutoCommit(autoCommit); }- 在该方法中,我们可以看到,已经开启了日志的打印。
5.3 commit
#commit() 方法,提交事务。代码如下:
java复制// JdbcTransaction.java
@Override
public void commit() throws SQLException {
// 连接存在,并且非自动提交,进行提交事务
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}
5.4 rollback
#rollback() 方法,回滚事务。代码如下:
java复制// JdbcTransaction.java
@Override
public void rollback() throws SQLException {
// 连接存在,并且非自动提交,进行回滚事务
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
connection.rollback();
}
}
5.5 close
#close() 方法,关闭连接。代码如下:
java复制// JdbcTransaction.java
@Override
public void close() throws SQLException {
if (connection != null) {
