raise_application_error是pl/sql内置过程,必须在begin和exception之间的可执行块中调用,错误号须为-20000至-20999间固定值,消息非空且≤2048字节,方可穿透至客户端供业务分类处理。

RAISE_APPLICATION_ERROR必须在可执行块里调用
它不是函数,是PL/SQL内置过程,不能放在声明区、SQL表达式或触发器的COMMIT之后。常见错误是写在DECLARE段末尾,或者像这样嵌在SELECT ... INTO语句里:SELECT RAISE_APPLICATION_ERROR(-20001, 'xxx') FROM dual——语法直接报错。
正确位置只有一处:BEGIN和EXCEPTION之间(即可执行部分)。触发器里也一样,但注意:抛错后别跟COMMIT,否则会触发ORA-06519。
- 存储过程、函数、匿名块、触发器中都可用,但必须在
BEGIN之后 - 不能在视图定义、物化视图刷新逻辑、DDL语句中直接调用
- 若封装在包内,调用方仍需确保在可执行上下文中触发
错误号必须是-20000到-20999之间的字面量或变量
传-1000或-30000会编译失败,报PLS-00302;传动态计算值如-20000 + v_offset虽能编译,但运行时越界才报ORA-21000,难排查。客户端靠错误号做业务分流,所以必须固定、可追溯。
推荐做法是建包常量,比如:pkg_err.C_USER_EXISTS CONSTANT PLS_INTEGER := -20001;。别用EXECUTE IMMEDIATE拼错误号——失去编译期检查,上线后才发现编号无效。
- 范围外的值不会静默忽略,而是直接中断编译或运行
- 空消息(
'')触发ORA-20000,消息长度超2048字节会被截断且不报错 - 第三个参数
keep_error_stack设为TRUE可保留原始调用栈(Oracle 10gR2+),默认FALSE只显示当前行
Java客户端捕获不到-20001?检查JDBC连接参数
默认情况下,JDBC驱动把RAISE_APPLICATION_ERROR映射成SQLException,但getErrorCode()返回的是17001或0,原始错误号丢失。这不是代码问题,是驱动配置缺失。
必须在数据库连接URL里显式启用错误码透传:oracle.jdbc.RetainV9Binding=true(12c及以后建议配合oracle.jdbc.fanEnabled=false)。Spring里若上层@Transactional方法吞了异常没re-throw,错误码就彻底不可见。
- 调试时先用
SQL*Plus执行对应过程,确认SHOW ERRORS和实际报错一致 - Java侧必须用
SQLException.getErrorCode(),不是getSQLState() -
DBMS_OUTPUT.PUT_LINE在生产环境通常被禁用,别依赖它传错误信息
校验失败后别忘了终止后续逻辑
常见坑:IF校验失败后调用RAISE_APPLICATION_ERROR,但没加RETURN(函数)或没提前EXIT(循环内),导致后面代码继续执行,可能造成数据不一致或二次报错掩盖原意。
例如函数里写:IF p_id IS NULL THEN RAISE_APPLICATION_ERROR(-20002, 'ID不能为空'); END IF; INSERT ...——这里INSERT仍会执行。应改为IF p_id IS NULL THEN RAISE_APPLICATION_ERROR(-20002, 'ID不能为空'); RETURN; END IF;
- 用
IF NOT condition THEN ... END IF;比多层ELSE更清晰 - 日志记录要放在
RAISE_APPLICATION_ERROR之前,否则抛错后事务回滚,日志可能写不进去 - 触发器里抛错会强制回滚整个DML事务,无需也不该手动
ROLLBACK
RAISE_APPLICATION_ERROR,而是确保错误号在全系统唯一、客户端能稳定拿到、抛错后逻辑不继续跑偏——这三件事串起来,才是“正确使用”。











