MyBatis中真正的SQLException被多层包装,需通过遍历getCause()链或Spring的DataAccessUtils.findException获取;非SQL执行类异常(如BindingException)不含SQLException。
mybatis执行失败时,真正的sqlexception通常被层层包装,不会直接抛出。要获取原始的sqlexception,关键是从异常链中逐级向上查找,直到找到类型为java.sql.sqlexception或其子类的异常实例。
查看异常根因:遍历getCause()链
MyBatis默认将底层JDBC异常封装为org.apache.ibatis.exceptions.PersistenceException,再嵌套org.apache.ibatis.executor.ExecutorException,最终cause才是真正的SQLException。可通过递归调用getCause()定位:
- 从捕获的异常开始,持续调用
getCause(),直到返回null或遇到SQLException实例 - 注意:某些场景下(如连接池超时),实际SQLException可能被包装在
RuntimeException中,需同时检查getCause()和异常本身类型 - 示例代码片段:
Throwable t = e;
while (t != null && !(t instanceof SQLException)) {
t = t.getCause();
}
if (t instanceof SQLException) {
// 处理该SQLException
}
使用Spring环境下的统一提取方式
若项目集成Spring(如使用SqlSessionTemplate或@Mapper),Spring会进一步包装异常为DataAccessException(如JdbcSQLException)。此时推荐使用Spring提供的工具方法:
- 调用
org.springframework.dao.support.DataAccessUtils#findException,传入异常对象,自动遍历并返回最内层的SQLException - 或直接使用
org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator解析错误码,无需手动解包 - Spring Boot中还可通过
@ControllerAdvice统一处理,用ExceptionUtils.getRootCause(e)快速获取根源异常
日志与调试技巧:启用MyBatis详细日志
仅靠异常链有时难以定位问题源头,配合日志可确认SQL执行上下文:
- 在
logback.xml或logging.properties中开启org.apache.ibatis和java.sql相关包的DEBUG级别日志 - 重点关注
jdbc.sqlonly、jdbc.resultset等日志开关(依赖具体JDBC驱动支持,如HikariCP+MySQL可结合logQuery参数) - 异常堆栈中若出现
Connection refused、Lock wait timeout等关键词,基本可判定是SQLException,再结合getCause()验证即可
避免常见误判:不是所有异常都含SQLException
并非所有MyBatis异常都源自JDBC层,需区分场景:
立即学习“Java免费学习笔记(深入)”;
-
BindingException:Mapper接口与XML/注解不匹配,无SQLException -
TooManyResultsException:查询结果多于预期,属于MyBatis逻辑异常,非SQL执行失败 -
ExecutorException若cause为NullPointerException,说明是参数或配置问题,非数据库异常 - 只有涉及SQL执行、事务提交、连接获取失败等情况,才可能包裹SQLException


















