目录
[核心思路:在 MyBatis 的"执行入口"设卡](#核心思路:在 MyBatis 的“执行入口”设卡)
[第一步:找到 `SqlSessionTemplate`](#第一步:找到 SqlSessionTemplate)
[第二步:在 `invoke` 方法上打个断点](#第二步:在 invoke 方法上打个断点)
[第三步:Debug 启动 + 触发查询](#第三步:Debug 启动 + 触发查询)
[第四步:找到 `BoundSql`,看 `sql` 字段](#第四步:找到 BoundSql,看 sql 字段)
[快速求值:Alt + F8](#快速求值:Alt + F8)
[条件断点:只抓你想看的 SQL](#条件断点:只抓你想看的 SQL)
[想看到带参数的完整 SQL?可以,但要小心](#想看到带参数的完整 SQL?可以,但要小心)
写过 MyBatis-Plus 的人都懂:`LambdaQueryWrapper` 用起来是真香,链式调用几行代码就能搞定一堆条件。但一旦结果不对,就容易懵------到底是条件写错了?还是 MP 根本没按你想的拼 SQL?
这时与其靠猜、靠日志、靠反复改代码试,不如直接在 IDEA 里打断点,看看它到底生成了什么 SQL。
为什么日志不够用?
很多人第一反应是开日志:
bash
logging:
level:
com.yourpackage.mapper: debug
这样做确实能看到 SQL,但问题也明显:
- 日志里全是 `?`,参数得自己脑补;
- 一堆无关 SQL 刷屏,找目标语句像大海捞针;
- 动态条件拼接逻辑藏在背后,看不出"为什么没拼上某个条件"。
而断点调试可以:
- 精准定位到某一次查询;
- 看到完整的、带结构的 SQL 字符串;
- 顺带检查参数、条件对象、执行上下文。
核心思路:在 MyBatis 的"执行入口"打断点调式
MyBatis-Plus 底层还是 MyBatis,所有 SQL 最终都会经过 SqlSessionTemplate 的 invoke 方法。我们就在这里"蹲点"。
第一步:找到 `SqlSessionTemplate`
在 IDEA 里按 Ctrl + N(Mac 是 `Cmd + O`),输入 SqlSessionTemplate,选 org.mybatis.spring.SqlSessionTemplate。
> 别选错包!确保是 `mybatis-spring` 里的,不是你自己写的。
第二步:在 `invoke` 方法上打个断点
这个方法长这样:
java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ...
}
第三步:Debug 启动 + 触发查询
- Debug 模式启动;
- 调用你怀疑有问题的接口或方法;
- 程序会在 `invoke` 处暂停。
第四步:找到 `BoundSql`,看 `sql` 字段
在 Debug 面板里,展开调用栈,找到类似这样的路径:
SqlSessionTemplate.invoke()
→ SqlSessionInterceptor.invoke()
→ DefaultSqlSession.selectList()
→ ...
→ BoundSql
点开 BoundSql 对象,里面的 `sql` 属性就是 MP 拼出来的完整 SQL。
比如你写了:
java
wrapper.eq(User::getStatus, 1).like(User::getName, "张");
这里就能看到:
sql
SELECT id, name, status FROM user WHERE status = ? AND name LIKE ?
虽然还是 `?`,但至少确认了条件拼对了没。
进阶技巧:让调试更高效
快速求值:Alt + F8
在断点暂停时,按 `Alt + F8`(Mac 是 `Option + F8`),直接输入:
java
boundSql.getSql()
回车,立刻出结果,不用一层层点。
条件断点:只抓你想看的 SQL
右键断点 → **More** → 勾选 **Condition**,输入:
java
method.getName().contains("selectList") && args[0].toString().contains("UserMapper")
这样只有查用户列表时才停,避免被其他 SQL 打断节奏。
临时打印条件片段(开发阶段用)
在代码里临时加一行:
java
System.out.println("条件片段: " + wrapper.getSqlSegment());
输出类似:
条件片段: WHERE status = ? AND name LIKE ? ORDER BY create_time DESC
适合快速验证条件是否生效,不用进 Debug。
想看到带参数的完整 SQL?可以,但要小心
MyBatis 默认用预编译(PreparedStatement),所以 SQL 里是 `?`。参数存在 parameterObject 或 parameterMappings 中。
如果你真想拼成完整 SQL(比如 `WHERE name = '张三'`),可以用这个简化版工具类(仅用于本地调试!):
java
public class DebugSqlHelper {
public static String toCompleteSql(BoundSql boundSql, Configuration config) {
String sql = boundSql.getSql().replaceAll("\\s+", " ");
Object param = boundSql.getParameterObject();
List<ParameterMapping> mappings = boundSql.getParameterMappings();
if (mappings == null || mappings.isEmpty()) {
return sql;
}
MetaObject metaParam = config.newMetaObject(param);
for (ParameterMapping mapping : mappings) {
String prop = mapping.getProperty();
Object value = metaParam.getValue(prop);
// 简单处理,实际需考虑类型、转义等
sql = sql.replaceFirst("\\?", "'" + value + "'");
}
return sql;
}
}
在 `Alt + F8` 里调用:
java
DebugSqlHelper.toCompleteSql(boundSql, configuration)
注意:这仅用于开发调试!别提交到 Git,更别放生产。
其他方案
MyBatis Log Plugin可以自动格式化 SQL,但插件兼容性差,新版 IDEA 有时失效
p6spy:记录所有 SQL,配置麻烦,日志量大
开启 debug 日志:信息杂乱不容易找,并且看不到sql参数
最后提醒几个坑
- **断点不触发?**检查是不是 Run 而不是 Debug;检查依赖是否冲突(比如引入了多个 MyBatis 版本)。
- **SQL 看不到?**确认查询逻辑真的执行了(比如事务回滚、缓存命中可能跳过 SQL)。
参考文档
结语
调试不是"救火",而是开发的基本功。能快速看到 SQL,很多时候比读十遍文档都管用。
希望这个方法能帮你少走弯路。如果你有更骚的操作,也欢迎在评论区分享!
> 本文适用于 Spring Boot + MyBatis-Plus 3.5+ 项目,亲测有效。觉得有用?点个赞再走~