MyBatis 不自动将手动 throw 的异常转为 PersistenceException,仅对 JDBC 抛出的 SQLException(如 SQLTimeoutException)通过 SQLExceptionTranslator 转换;超时需由 JDBC 驱动真实触发,且须配置 timeout 参数或使用 Spring 的 MyBatisExceptionTranslator。

MyBatis 本身不会自动把 throw 抛出的异常(比如你手动 throw 的 SQLException 或运行时异常)转成 PersistenceException,更不会因为“执行超时”就自动转换——超时本身需由底层 JDBC 驱动或连接池触发,MyBatis 只负责包装和传递。
要实现「MyBatis 执行超时 → 转为 PersistenceException」,关键在于:超时必须真实发生(如 JDBC 设置了 queryTimeout),且 MyBatis 的异常翻译机制被启用。而你手动 throw 的异常,默认不会走 MyBatis 的翻译流程。
确保 MyBatis 异常翻译生效
MyBatis 默认通过 SQLExceptionTranslator 将 JDBC 异常转为 PersistenceException 子类(如 SQLExecutorException、DataAccessException 等)。但该机制只对 SqlSession 执行过程中抛出的 SQLException 生效,不处理你代码里手动 throw 的任意异常。
- 确认配置了
<setting name="callSettersOnNulls" value="true"/>不影响异常翻译,真正相关的是是否启用了异常翻译器(默认开启) - Spring 整合 MyBatis 时,若使用
SqlSessionFactoryBean,它会自动注册MyBatisExceptionTranslator,将底层异常包装为PersistenceException - 纯 MyBatis(无 Spring)下,需自行调用
ExceptionFactory或继承DefaultExceptionTranslator实现转换
让超时真正触发 SQLException
MyBatis 无法凭空感知“超时”,必须依赖 JDBC 驱动返回超时异常(通常是 SQLTimeoutException,它是 SQLException 子类)。你需要在语句级或全局设置查询超时:
立即学习“Java免费学习笔记(深入)”;
- XML 映射中设置:
<select id="xxx" timeout="5">(单位秒) - 注解方式:
@Select("...") @Options(timeout = 5) - Java API 调用时传参:
sqlSession.selectList("xxx", null, new RowBounds(), statementTimeout) - 确保数据库连接池(如 HikariCP)未覆盖或禁用 JDBC 的 timeout 机制(例如 HikariCP 的
connection-timeout控制获取连接超时,不是 SQL 执行超时)
手动 throw 的异常如何转为 PersistenceException
如果你在拦截器、TypeHandler 或自定义逻辑中 throw new RuntimeException("timeout"),这不会被 MyBatis 捕获翻译。要统一处理,可:
- 在 Service 层 catch 原始异常,再 re-throw
new PersistenceException("SQL timeout", cause) - 写一个 MyBatis
Interceptor,在plugin中 catch 异常,判断是否为超时相关(如 message 含 "timeout"、cause 是SQLTimeoutException),然后包装成PersistenceException - Spring 环境下,用
@ExceptionHandler统一将特定异常转为DataAccessException(PersistenceException的 Spring 等价抽象)
验证是否成功转换
实际抛出的异常类型决定了是否进入翻译流程:
- ✅ 正确路径:
SQLTimeoutException→ MyBatisSQLExceptionTranslator→PersistenceException(或 Spring 的org.springframework.dao.QueryTimeoutException) - ❌ 错误路径:
throw new RuntimeException("timeout")→ 直接向上抛,不会变成PersistenceException - 调试时打印异常栈,确认顶层异常是否为
org.apache.ibatis.exceptions.PersistenceException或其子类
不复杂但容易忽略:超时得由 JDBC 层真实抛出 SQLException,MyBatis 才有“原料”去翻译;手动 throw 的异常需要你自己桥接,不能指望框架自动识别语义。


















