raise exception必须显式指定5位sqlstate码(如'p0001')才能被java等客户端精准识别为业务异常;仅写raise exception 'msg'依赖默认码且不可靠,而exception块不能声明自定义异常名,只能用using errcode触发。

用 RAISE EXCEPTION 抛出自定义错误信息
PostgreSQL 不支持像 Oracle 那样先声明异常再 RAISE,而是直接用 RAISE EXCEPTION 语句构造带 SQLSTATE 的错误。SQLSTATE 是 5 位字符串,自定义时推荐用 P0001(PostgreSQL 官方保留的通用用户错误码),也可用 XX000 起头的未分配码。
RAISE EXCEPTION '用户输入无效:订单金额不能为负' USING ERRCODE = 'P0001';- 客户端收到的
SQLState就是P0001,便于 Java 等应用层精准识别 - 避免只写
RAISE EXCEPTION 'xxx'——没指定ERRCODE时默认为P0001,但显式写出更可靠 - 不要用
RAISE NOTICE或RAISE WARNING替代——它们不会中断执行,也无法被客户端捕获为异常
在 Java 中捕获并区分自定义错误
Java 的 SQLException 提供 getSQLState() 方法,必须靠它判断是否是你的业务异常,而不是靠 getMessage() 字符串匹配(易受翻译、格式变动影响)。
- 调用存储过程后,
catch (SQLException e)中检查e.getSQLState().equals("P0001") - 若匹配,说明是主动抛出的业务校验失败,可转为
BusinessException向上抛 - 注意:PostgreSQL JDBC 驱动不会把
RAISE NOTICE当作异常,所以 Java 侧收不到 - 如果存储过程里用了
RAISE EXCEPTION ... USING ERRCODE = '23505'(唯一约束冲突),Java 收到的是标准23505,不是你写的业务逻辑
为什么不能用 EXCEPTION 块“重抛”自定义错误
PL/pgSQL 的 EXCEPTION 块本质是错误处理分支,不是异常声明机制。你无法在 DECLARE 区定义一个叫 e_invalid_input 的异常变量,然后在 RAISE e_invalid_input ——这会报错 ERROR: unrecognized exception。
- 所有自定义错误都必须用
RAISE EXCEPTION ... USING ERRCODE直接触发 -
EXCEPTION块里可以再次RAISE,但只能复用原错误或构造新错误,不能引用未声明的“异常名” - 想统一错误码?直接在多个
RAISE EXCEPTION处硬编码相同ERRCODE,比如全用'P0001' - 别试图模仿 Oracle 的
PRAGMA EXCEPTION_INIT——PostgreSQL 没这个语法
容易被忽略的细节:客户端是否真能看到错误
不是所有客户端都能完整接收 RAISE EXCEPTION 的全部字段。比如 psql 默认只显示 MESSAGE,而 DETAIL 和 HINT 需要 \set SHOW_CONTEXT always 才可见;Java 应用则只暴露 SQLState、errorCode 和 message。
- 用
USING DETAIL = '原始参数值:'|| order_amount补充上下文,对排查有帮助 - 但别依赖
DETAIL在所有客户端一致显示——Java 里它通常合并进getMessage(),而 Web API(如 PostgREST)可能只映射MESSAGE到 HTTP body - 生产环境务必测试真实调用链:应用代码 → JDBC → PostgreSQL → 错误能否按预期被捕获和分类











