主要实现:
- 拦截sql执行
- 记录执行时间
- 获取sql信息
- 输出日志
我们的目的是为了更方便的在日志中查看sql执行情况
详细实现步骤:只需要创建一个配置类即可
类定义与注解
java
@Component
@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 MybatisLog implements Interceptor {
使用 @Component 将该类注册为 Spring 组件。
使用 @Intercepts 注解定义拦截规则,指定拦截 StatementHandler 的 query、update 和 batch 方法。
实现 Interceptor 接口,重写 intercept 方法以自定义拦截逻辑。
初始化日志记录器
java
private static final Logger log = LoggerFactory.getLogger(MybatisLog.class);
创建一个 SLF4J 日志记录器,用于后续输出 SQL 执行相关信息
拦截方法入口
java
@Override
public Object intercept(Invocation invocation) throws Throwable {
重写 intercept 方法,这是插件的核心逻辑入口。
参数 Invocation 包含了被拦截方法的所有信息(目标对象、方法名、参数等)。
获取目标对象和开始时间
java
Object target = invocation.getTarget();
long startTime = System.currentTimeMillis();
invocation.getTarget():获取当前被拦截的目标对象(这里是 StatementHandler)。
System.currentTimeMillis():记录 SQL 执行开始时间,用于计算耗时。
类型转换及异常处理
java
StatementHandler statementHandler = (StatementHandler) target;
try {
return invocation.proceed();
} finally {
...
}
将目标对象强制转换为 StatementHandler,以便访问其内部方法。
使用 try-finally 结构确保无论是否发生异常,都会执行日志记录逻辑。
记录结束时间和计算耗时
java
long endTime = System.currentTimeMillis();
long sqlCost = endTime - startTime;
提取sql相关信息
java
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
getBoundSql():获取 BoundSql 对象,包含原始 SQL 和参数信息。
getSql():提取原始 SQL 语句。
getParameterObject():获取传递给 SQL 的参数对象(可能是实体类或 Map)。
getParameterMappings():获取参数映射列表,描述每个占位符(如 ?)对应的参数信息。
输出日志
java
log.info("参数:[{}]", parameterObject);
log.info("参数映射:[{}]", parameterMappings);
log.info("SQL: [{}] 执行耗时:[{} ms]", sql, sqlCost);
完整代码:
java
package org.system.orderService.config;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.sql.Statement;
import java.util.List;
@Component
@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 MybatisLog implements Interceptor {
private static final Logger log = LoggerFactory.getLogger(MybatisLog.class);// 日志记录器
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 1. invocation.getTarget() 获取当前被拦截的目标对象
Object target = invocation.getTarget();
// 2. 获取当前时间
long startTime = System.currentTimeMillis();
// 3. 从StatementHandler中提取原始SQL、参数对象及参数映射列表
StatementHandler statementHandler = (StatementHandler) target;
try{
// 4. 拦截目标方法并执行
return invocation.proceed();
}finally {
long endTime = System.currentTimeMillis();
// 5. sql 执行耗时
long sqlCost = endTime - startTime;
// 6. 获取原始SQL
BoundSql boundSql = statementHandler.getBoundSql();
// 获取参数映射列表 SELECT * FROM user WHERE id = ? AND name = ?
String sql = boundSql.getSql();
// 获取参数对象
/*
* 如果是对象
* User user = new User();
* user.setId(1);
* user.setName("Alice");
* */
/**
* 如果Map
* Map<String, Object> params = new HashMap<>();
* params.put("id", 1);
* params.put("name", "Alice");
*/
Object parameterObject = boundSql.getParameterObject();
// 获取参数映射列表
/**
* ? 占位符的参数列表
* [
* ParameterMapping{property='id', mode=IN, javaType=int, jdbcType=INTEGER},
* ParameterMapping{property='name', mode=IN, javaType=string, jdbcType=VARCHAR}
* ]
*/
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
// 7. 格式化sql语句
// 8. 输出日志
log.info("参数:[{}]",parameterObject);
log.info("参数映射:[{}]",parameterMappings);
log.info("SQL: [{}] 执行耗时:[{} ms]",sql,sqlCost );
}
}
}