MyBatis Plus 输出完整 SQL(带参数)的 3 种方案
📌 目录
- ❓ 为什么 MyBatis Plus 默认 SQL 日志没有参数?
- 🚀 方案 1:使用 `SqlLogInterceptor`(推荐 ✅)
- 🛠 方案 2:自定义 MyBatis `Interceptor`
- **编写 `Interceptor`:**
- 🔍 方案 3:开启 MyBatis 日志(手动拼接 SQL)
- ⚖️ 最佳方案对比与选择
- 🎯 结论:哪种方式适合你?
❓ 为什么 MyBatis Plus 默认 SQL 日志没有参数?
当你使用 mybatis-plus
时,可能会遇到这样的情况:
Preparing: SELECT id, name, `desc` FROM author WHERE name = ?
Parameters: 刘禹锡(String)
这导致 SQL 不能直接执行,调试也不方便。那么,如何打印完整 SQL(带参数)呢?本篇文章将介绍 3 种实现方式,并对比它们的优缺点。
🚀 方案 1:使用 SqlLogInterceptor
(推荐 ✅)
从 MyBatis Plus 3.5.3 版本 开始,官方提供了 SqlLogInterceptor
,可以自动替换 SQL 语句中的 ?
为实际参数。
配置方式:
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.SqlLogInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new SqlLogInterceptor()); // ✅ 让 SQL 带参数
return interceptor;
}
}
效果:
Executing SQL: SELECT id, name, `desc` FROM author WHERE name = '刘禹锡'
✅ 优点:
- 官方支持,无侵入,简单易用
- 低性能开销,仅影响日志输出
- 适用于 MyBatis Plus
🚨 缺点:
- 只适用于 MyBatis Plus(普通 MyBatis 需要用方案 2)
🛠 方案 2:自定义 MyBatis Interceptor
如果你使用的是 原生 MyBatis,或者想要更灵活的日志格式,可以 自定义 Interceptor
。
编写 Interceptor
:
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {
MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {
MappedStatement.class, Object.class})
})
@Slf4j
public class SqlInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = invocation.proceed();
long endTime = System.currentTimeMillis();
String sql = generateSql(invocation);
log.info("\n 执行SQL耗时:{}ms \n 执行SQL:{}", endTime - startTime, sql);
return proceed;
}
private static String generateSql(Invocation invocation) {
MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
Object parameter = invocation.getArgs().length > 1 ? invocation.getArgs()[1] : null;
BoundSql boundSql = statement.getBoundSql(parameter);
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
for (ParameterMapping param : boundSql.getParameterMappings()) {
Object value = boundSql.getAdditionalParameter(param.getProperty());
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(value)));
}
return sql;
}
private static String getParameterValue(Object object) {
return object instanceof String ? "'" + object + "'" : String.valueOf(object);
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
✅ 优点:
- 适用于 原生 MyBatis 和 MyBatis Plus
- 可自定义日志格式,控制输出内容
🚨 缺点:
- 需要手动编写,稍微复杂一点
- 性能开销略大(需要解析 SQL 并替换参数)
🔍 方案 3:开启 MyBatis 日志(手动拼接 SQL)
如果只是 临时调试,可以在 application.yml
配置 MyBatis 日志:
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
然后,手动拼接 SQL 进行调试。
✅ 优点:
- 无代码改动,适合临时调试
🚨 缺点:
- 仍然是
Preparing: xxx
和Parameters: xxx
的分离格式 - 不能直接复制执行
⚖️ 最佳方案对比与选择
方案 | 适用场景 | 侵入性 | 性能开销 | 适用框架 |
---|---|---|---|---|
SqlLogInterceptor ✅ | MyBatis Plus | 无 | 低 | MyBatis Plus |
自定义 Interceptor 🚀 | 需要特殊格式 | 中等 | 中 | MyBatis & MyBatis Plus |
MyBatis 日志 🛠 | 临时调试 | 无 | 低 | MyBatis & MyBatis Plus |
🎯 结论:哪种方式适合你?
- 推荐 ✅:使用
SqlLogInterceptor
,简单、无侵入,适用于 MyBatis Plus。 - 进阶 🚀:自定义
Interceptor
,适用于 MyBatis,可自定义日志格式。 - 临时调试 🛠:开启 MyBatis 日志,但不自动替换
?
。