Oracle自定义异常在Java中以SQLException形式抛出,需通过getErrorCode()获取负值错误码(如-20101)精准识别,不可依赖getMessage()或getSQLState(),Spring中需先getCause()再提取,MyBatis可直接捕获。

Oracle自定义异常在Java中抛出的是SQLException
Oracle里用 RAISE_APPLICATION_ERROR(-20001, '用户不存在') 抛出自定义错误时,JDBC驱动不会转成独立异常类型,而是封装进标准的 SQLException。关键在于从它的错误码(SQLState 或 ErrorCode)里提取原始 Oracle 错误号。
注意:Oracle 的自定义错误号范围是 -20000 到 -20999,而系统错误(如 ORA-00942)是正数或不同前缀。别混淆 getErrorCode() 和 getSQLState() —— 前者返回 Oracle 原生错误码(含负号),后者通常是 "72000" 这类通用码,不可靠。
用getErrorCode()判断是否为特定自定义异常
直接检查 SQLException.getErrorCode() 是否等于你定义的负值即可,这是最稳定的方式。
- 如果 PL/SQL 中写了
RAISE_APPLICATION_ERROR(-20101, '余额不足'),Java 中就写if (e.getErrorCode() == -20101) - 不要用
getMessage()做字符串匹配——消息可能被国际化、被日志框架截断或带堆栈前缀 - 多个自定义异常可链式判断:
if (e.getErrorCode() == -20101) { ... } else if (e.getErrorCode() == -20102) { ... }
捕获后别直接吞掉异常,要保留原始上下文
很多人在 catch 里只打印日志或返回 null,结果线上出问题时查不到是哪个存储过程、哪条语句触发了 -20105。至少得把原始 SQLException 包装再抛出,或记录完整堆栈 + 错误码。
立即学习“Java免费学习笔记(深入)”;
- 推荐做法:
throw new BusinessException("余额校验失败", e);,其中BusinessException是你自己的运行时异常 - 如果必须转换,用
e.getErrorCode()构造新异常信息,例如"ERR_20101: " + e.getMessage() - 避免调用
e.printStackTrace()—— 它不走日志框架,容易丢失时间戳和线程信息
Spring JDBC 和 MyBatis 的差异处理
Spring JDBC 默认会把 SQLException 转成 DataAccessException 层级(如 UncategorizedSQLException),原始错误码藏在 cause 里;MyBatis 则默认原样抛出 SQLException。
- Spring 场景下,必须先
getCause()再强转:if (e.getCause() instanceof SQLException s) { s.getErrorCode(); } - MyBatis 可直接 catch
SQLException,但注意配置了exceptionTranslator就可能被拦截 - 无论哪种框架,都别依赖
@ExceptionHandler(SQLException.class)全局捕获——它会盖掉所有数据库底层异常,包括连接超时、死锁等真正需要区分处理的问题
RAISE_APPLICATION_ERROR 的第一个参数写成正数(比如 20001),Oracle 会自动转成负值,但 Java 侧仍要按 -20001 判断;另外,有些 DBA 会在异常消息里拼接动态值(如 "用户ID=123不存在"),这时候更不能靠字符串匹配。


















