mybatis mapper接口方法不应声明throws sqlexception,因其通过动态代理将sqlexception包装为unchecked的persistenceexception抛出;正确写法是不声明异常,由调用方捕获运行时异常并根据cause判断具体sql错误。

MyBatis 的 Mapper 接口方法中不能也不应该声明 throws SQLException。
MyBatis 会自动处理底层 SQL 异常
MyBatis 将 JDBC 的 checked 异常(如 SQLException)统一包装为 unchecked 的 RuntimeException 子类,主要是 org.apache.ibatis.exceptions.PersistenceException,其根本原因通常是 SQLException,但已转为运行时异常向上抛出。
- Mapper 接口是纯接口,不包含实现,MyBatis 通过动态代理生成实现类,该实现在执行 SQL 时捕获并转换异常
- 因此你在接口方法签名里写
throws SQLException不仅编译不过(因为代理实现没声明该异常),也违背 MyBatis 的异常设计原则 - 即使强行在接口加 throws,编译器会报错:*unreported exception SQLException; must be caught or declared to be thrown*
正确做法:不声明异常,由调用方捕获运行时异常
Mapper 方法应保持简洁,不 throws 任何 checked 异常:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
// ✅ 正确写法(无 throws) User selectUserById(int id); // ❌ 错误写法(编译失败) User selectUserById(int id) throws SQLException;
- 实际调用时若 SQL 出错(如表不存在、字段名错误、连接超时),会抛出
PersistenceException(可 cause 是SQLException) - 业务层或 Controller 中按需捕获
PersistenceException或更通用的RuntimeException,并提取 root cause 判断是否为SQLException - 例如:
if (e.getCause() instanceof SQLException)
需要区分 SQL 错误场景?用 try-catch 提取 cause
如果必须针对特定 SQL 错误码(如 MySQL 的 1062 主键冲突)做处理,应在调用处处理:
- 捕获
PersistenceException - 调用
e.getCause()获取原始SQLException - 再通过
sqlEx.getSQLState()或sqlEx.getErrorCode()做分支判断
想强制检查异常?这不是 MyBatis 的风格
MyBatis 故意屏蔽 checked 异常,是为了简化 DAO 层代码。如果你坚持使用 checked 异常:
- 可自定义拦截器或扩展 Executor,在异常抛出前重新包装成 checked 异常 —— 但破坏了标准用法,增加维护成本
- 或在外层 Service 方法中声明 throws,并在内部 try-catch 后显式 throw 自定义 checked 异常 —— 异常语义已脱离 MyBatis 原始上下文
- 不推荐。Spring + MyBatis 生态默认基于 RuntimeException 处理事务回滚和统一异常翻译
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










