mysql原生不支持try...catch语法,使用declare...handler机制处理错误;必须在出错语句前声明,支持continue或exit类型,配合sqlstate或sqlexception捕获异常,并需注意事务显式控制与诊断信息获取。

MySQL 存储过程没有 TRY...CATCH 语法
直接说结论:MySQL 原生不支持 TRY...CATCH 这种结构,它用的是 DECLARE ... HANDLER 机制来响应错误。很多刚从 SQL Server 或 Java 转过来的人会下意识写 BEGIN TRY,结果报错 ERROR 1064 (42000) —— 这是因为 MySQL 解析器根本认不出这个语法。
用 DECLARE HANDLER 捕获特定 SQLSTATE 或错误码
MySQL 的错误处理核心是声明一个“处理器”,指定当某类错误发生时执行什么逻辑。关键点在于:必须在出错语句**之前**声明 handler,且 handler 只对后续语句生效。
常见写法有两类:
-
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000':捕获唯一键冲突(如重复插入) -
DECLARE EXIT HANDLER FOR SQLEXCEPTION:捕获所有未被更具体 handler 拦截的异常,类似 Java 的catch (Exception e)
注意:SQLEXCEPTION 不是字符串,不能加引号;而 SQLSTATE '23000' 中的 '23000' 是标准 SQL 状态码,必须带单引号。
示例片段:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
DELIMITER $$
CREATE PROCEDURE safe_insert(IN p_id INT, IN p_name VARCHAR(50))
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL; -- 重新抛出原错误,便于调用方感知
END;
<p>START TRANSACTION;
INSERT INTO users(id, name) VALUES(p_id, p_name);
COMMIT;
END$$
DELIMITER ;</p>
为什么不能只靠 EXIT HANDLER 处理所有场景
因为 EXIT HANDLER 触发后会立即退出当前存储过程作用域,中间状态不可控。比如你希望在主键冲突时改走更新逻辑,而不是直接失败——这时就得用 CONTINUE HANDLER + 标志变量:
- 先
DECLARE duplicate_flag TINYINT DEFAULT 0 - 再
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET duplicate_flag = 1 - 执行
INSERT后检查IF duplicate_flag = 1 THEN UPDATE ...
漏掉这一步,就只能回滚或放弃,没法做“降级操作”。另外,RESIGNAL 和 SIGNAL 在 MySQL 5.5+ 才支持,老版本只能靠 SELECT 'error occurred' 或写日志表模拟。
事务与 HANDLER 的配合容易踩坑
MySQL 存储过程中,事务控制和错误处理必须显式配对,否则可能产生静默失败:
-
START TRANSACTION后若没COMMIT或ROLLBACK,连接断开前事务一直挂着 -
EXIT HANDLER中只写ROLLBACK不够,还要确保不会因二次错误导致事务残留 - 不要依赖
@@error或@@sqlstate—— 它们只反映上一条语句,且在 handler 内部可能已被覆盖
最稳妥的做法是在 EXIT HANDLER 开头加 GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO;,再根据 @sqlstate 做分支处理。这点常被忽略,但线上定位问题时非常关键。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










