java
复制代码
package com.sunxiansheng.mybatis.plus.inteceptor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.vertical_blank.sqlformatter.SqlFormatter;
import com.github.vertical_blank.sqlformatter.languages.Dialect;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.sql.Statement;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
/**
* SQL美化拦截器:显示完整的SQL,并输出执行结果
*/
@Intercepts({
@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})
})
public class SqlBeautyInterceptor implements Interceptor {
private static final Logger logger = LoggerFactory.getLogger(SqlBeautyInterceptor.class);
// ANSI 颜色代码
private static final String ANSI_RESET = "\u001B[0m";
private static final String ANSI_BRIGHT_GREEN = "\u001B[92m"; // 亮绿色(结果)
private static final String ANSI_BRIGHT_BLUE = "\u001B[94m"; // 亮蓝色(项目名称)
// Gson 实例,用于格式化结果
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.create();
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
// 分离代理对象链
while (metaObject.hasGetter("h")) {
Object object = metaObject.getValue("h");
metaObject = SystemMetaObject.forObject(object);
}
// 分离最后一个代理对象的目标类
while (metaObject.hasGetter("target")) {
Object object = metaObject.getValue("target");
metaObject = SystemMetaObject.forObject(object);
}
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
Configuration configuration = mappedStatement.getConfiguration();
long startTime = System.currentTimeMillis();
Object result = null;
try {
// 执行 SQL,获取结果
result = invocation.proceed();
return result;
} finally {
long endTime = System.currentTimeMillis();
long sqlCost = endTime - startTime;
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
// 使用修改后的 formatSql 方法
String formattedSql = formatSql(configuration, boundSql);
// 格式化执行结果
String formattedResult = formatResult(result, sqlCommandType);
// 日志输出
logger.info("\n========================\n{}SQL 类型:{} {}{}{}\n{}SQL:{}\n{}{}{}\n{}执行耗时:{} {}{}ms{} \n{}结果:{}\n{}{}\n{}========================{}",
ANSI_BRIGHT_BLUE, ANSI_RESET,
ANSI_BRIGHT_GREEN, sqlCommandType, ANSI_RESET,
ANSI_BRIGHT_BLUE, ANSI_RESET,
ANSI_BRIGHT_GREEN, formattedSql, ANSI_RESET,
ANSI_BRIGHT_BLUE, ANSI_RESET,
ANSI_BRIGHT_GREEN, sqlCost, ANSI_RESET,
ANSI_BRIGHT_BLUE, ANSI_RESET,
ANSI_BRIGHT_GREEN, formattedResult, ANSI_RESET,
ANSI_BRIGHT_BLUE, ANSI_RESET);
}
}
@Override
public Object plugin(Object target) {
// 使用 Plugin.wrap 生成代理对象
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可以通过配置文件设置属性
}
/**
* 格式化 SQL,将参数替换到 SQL 中,并使用 SQLFormatter 进行格式化
*
* @param configuration MyBatis 配置对象
* @param boundSql 绑定的 SQL
* @return 格式化后的 SQL
*/
private String formatSql(Configuration configuration, BoundSql boundSql) {
try {
// 获取 SQL 语句
String sql = boundSql.getSql();
// 获取参数对象
Object parameterObject = boundSql.getParameterObject();
// 获取参数映射
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null && !parameterMappings.isEmpty() && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
MetaObject metaObject = configuration.newMetaObject(parameterObject);
// 替换 SQL 中的参数占位符
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
Object value;
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (metaObject.hasGetter(propertyName)) {
value = metaObject.getValue(propertyName);
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
value = null;
}
sql = replaceFirstQuestionMark(sql, getParameterValue(value));
}
}
// 使用 SQLFormatter 格式化 SQL
return SqlFormatter.of(Dialect.MySql).format(sql);
} catch (Exception e) {
logger.error("Error formatting SQL: ", e);
return boundSql.getSql();
}
}
private String replaceFirstQuestionMark(String sql, String value) {
return sql.replaceFirst("\\?", Matcher.quoteReplacement(value));
}
private String getParameterValue(Object obj) {
if (obj == null) {
return "null";
}
if (obj instanceof String) {
return "'" + obj + "'";
}
if (obj instanceof Number) {
return obj.toString();
}
if (obj instanceof java.util.Date || obj instanceof java.sql.Date || obj instanceof java.sql.Timestamp) {
return "'" + obj.toString() + "'";
}
return "'" + obj.toString() + "'";
}
/**
* 格式化 SQL 执行结果
*
* @param result 执行结果
* @param sqlCommandType SQL 类型
* @return 格式化后的结果字符串
*/
private String formatResult(Object result, SqlCommandType sqlCommandType) {
if (result == null) {
return "null";
}
try {
String formattedResult;
if (sqlCommandType == SqlCommandType.SELECT) {
// 查询语句,结果通常是 List
formattedResult = GSON.toJson(result);
} else if (sqlCommandType == SqlCommandType.UPDATE || sqlCommandType == SqlCommandType.DELETE || sqlCommandType == SqlCommandType.INSERT) {
// 更新语句,结果是受影响的行数
formattedResult = result.toString() + " 行受影响";
} else {
// 其他类型
formattedResult = result.toString();
}
// 可根据需要限制结果输出长度,避免日志过大
int maxLength = 2000;
if (formattedResult.length() > maxLength) {
formattedResult = formattedResult.substring(0, maxLength) + "... [结果过长,已截断]";
}
return formattedResult;
} catch (Exception e) {
logger.error("Error formatting result: ", e);
return result.toString();
}
}
}