Mybatis使用了动态代理来打印JDBC的执行日志。本文默认已经对Java动态代理有所了解。
Mybatis首先对所有java.sql.Connection进行代理,打印日志。返回代理对象,接下来sql参数的日志由PreparedStatement和Statement的代理对象完成。
ConnectionLogger:
if ("prepareStatement".equals(method.getName())) {
// 当调用prepareStatement,返回带日志功能的代理对象
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
//在这里返回PreparedStatement的代理对象
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("prepareCall".equals(method.getName())) {
// 调用prepareCall,返回带日志功能的代理对象
if (isDebugEnabled()) {
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())) {
// 调用createStatement,返回带日志Statement代理对象
Statement stmt = (Statement) method.invoke(connection, params);
stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
}
接下来是PreparedStatementLogger,这个类是对PreparedStatement生成带日志的代理。
package org.apache.ibatis.logging.jdbc;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.reflection.ExceptionUtil;
/**
* PreparedStatement动态代理,用于增加日志.
*
* @author Clinton Begin
* @author Eduardo Macarron
*/
public final class PreparedStatementLogger extends BaseJdbcLogger implements InvocationHandler {
private final PreparedStatement statement;
private PreparedStatementLogger(PreparedStatement stmt, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.statement = stmt;
}
/**
* 创建有日志版本的PreparedStatement
*
* @param stmt - the statement
* @param statementLog - the statement log
* @param queryStack - the query stack
* @return - the proxy
*/
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);
}
/** 在设置sql参数时把参数保存下来, 在执行之前打印sql参数日志, 并且结果集ResultSet进行日志代理*/
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
//EXECUTE_METHODS执行方法的静态常量,包括{"execute", "executeUpdate","executeQuery","addBatch"}
if (EXECUTE_METHODS.contains(method.getName())) {
//当调用执行方法时,打印参数日志,如果有返回结果,那么对ResultSet进行日志代理
if (isDebugEnabled()) {
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封装成日志代理对象
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else if ("getUpdateCount".equals(method.getName())) {
//调用getUpdateCount自动打印日志
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);
}
}
/**
* 返回包装的statement.
*
* @return the PreparedStatement
*/
public PreparedStatement getPreparedStatement() {
return statement;
}
}