Oracle中自定义异常内置异常嵌套异常的捕获处理
一、异常类型分类
类型 | 说明 | 示例 |
---|---|---|
内置预定义异常 | Oracle已命名异常(如NO_DATA_FOUND) | 查询无数据时触发 |
内置非预定义异常 | 未命名的Oracle错误(需用PRAGMA EXCEPTION_INIT关联) | ORA-02290(违反检查约束) |
自定义异常 | 用户定义的业务逻辑异常 | 数据校验失败时手动抛出 |
二、异常处理语法
1. 基本结构
DECLARE-- 声明自定义异常custom_exception EXCEPTION;-- 绑定错误代码到非预定义异常PRAGMA EXCEPTION_INIT(custom_exception, -20001);
BEGIN-- 业务逻辑IF 条件 THENRAISE custom_exception; -- 手动抛出异常END IF;
EXCEPTIONWHEN custom_exception THENDBMS_OUTPUT.PUT_LINE('自定义异常触发');WHEN NO_DATA_FOUND THENDBMS_OUTPUT.PUT_LINE('数据未找到');WHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('错误代码: ' || SQLCODE);DBMS_OUTPUT.PUT_LINE('错误信息: ' || SQLERRM);
END;
三、异常语法
1. 自定义异常
步骤说明:
①声明异常:
DECLAREinvalid_salary EXCEPTION;
②关联错误代码(可选):
PRAGMA EXCEPTION_INIT(invalid_salary, -20001);
③抛出异常:
IF salary < 0 THENRAISE invalid_salary;
END IF;
④捕获处理:
EXCEPTIONWHEN invalid_salary THENDBMS_OUTPUT.PUT_LINE('薪资不能为负数');
2. 内置异常处理
常用预定义异常:
NO_DATA_FOUND: SELECT未找到数据
TOO_MANY_ROWS: SELECT返回多行数据
ZERO_DIVIDE: 除数为零
DUP_VAL_ON_INDEX: 唯一索引冲突
-- 示例:
BEGINSELECT * INTO emp_rec FROM emp WHERE emp_id = 999;
EXCEPTIONWHEN NO_DATA_FOUND THENDBMS_OUTPUT.PUT_LINE('员工ID不存在');WHEN TOO_MANY_ROWS THENDBMS_OUTPUT.PUT_LINE('返回多行数据');
END;
3. 嵌套异常处理
结构示例:
DECLAREouter_exception EXCEPTION;
BEGIN<<inner_block>>DECLAREinner_exception EXCEPTION;BEGINRAISE inner_exception;EXCEPTIONWHEN inner_exception THENDBMS_OUTPUT.PUT_LINE('内部异常已处理');RAISE outer_exception; -- 抛出到外层END inner_block;
EXCEPTIONWHEN outer_exception THENDBMS_OUTPUT.PUT_LINE('外部捕获到异常');
END;
详细实例:
DECLARE-- 定义变量和异常v_employee_id employees.employee_id%TYPE := 100;v_salary employees.salary%TYPE;e_negative_salary EXCEPTION; -- 自定义异常:工资为负PRAGMA EXCEPTION_INIT(e_negative_salary, -20001); -- 绑定错误代码
BEGIN-- 外层块BEGIN -- 内层块开始-- 尝试获取员工工资SELECT salary INTO v_salary FROM employees WHERE employee_id = v_employee_id;-- 模拟业务逻辑:检查工资有效性IF v_salary < 0 THENRAISE_APPLICATION_ERROR(-20001, '工资不能为负数'); -- 触发自定义异常END IF;DBMS_OUTPUT.PUT_LINE('内层块:工资处理成功。');EXCEPTION -- 内层异常处理WHEN NO_DATA_FOUND THENDBMS_OUTPUT.PUT_LINE('内层块:员工ID ' || v_employee_id || ' 不存在。');RAISE; -- 重新抛出到外层WHEN e_negative_salary THENDBMS_OUTPUT.PUT_LINE('内层块:异常 - ' || SQLERRM);-- 此处可添加恢复逻辑,如设置默认工资v_salary := 0;DBMS_OUTPUT.PUT_LINE('内层块:工资已重置为0。');END; -- 内层块结束-- 外层业务逻辑(内层未异常终止时执行)DBMS_OUTPUT.PUT_LINE('外层块:更新后工资为 ' || v_salary);EXCEPTION -- 外层异常处理WHEN NO_DATA_FOUND THENDBMS_OUTPUT.PUT_LINE('外层块:错误 - 指定员工不存在。');WHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('外层块:未知错误 - ' || SQLERRM);ROLLBACK; -- 示例事务回滚
END;
嵌套结构:
- 内层块: 处理具体数据库操作(如查询、业务验证),捕获NO_DATA_FOUND(预定义)和自定义异常。
- 外层块: 处理内层未处理的异常,执行后续业务逻辑或全局错误处理。
异常传播:
- 内层处理并解决: 如e_negative_salary异常被捕获后重置工资,程序继续执行外层代码。
- 内层重新抛出: NO_DATA_FOUND在内层处理后通过RAISE传递到外层,触发外层对应处理程序。
自定义异常:
- 使用RAISE_APPLICATION_ERROR抛出并关联错误代码,PRAGMA
EXCEPTION_INIT将自定义异常绑定到特定错误号。
事务控制:
- 外层异常处理中可包含ROLLBACK,确保数据一致性(根据实际业务需求调整)。
嵌套规则:
- 内部块未处理的异常会自动传递到外层块
- 使用RAISE可手动将异常传递到上层
四、其他处理
1. 错误日志记录
EXCEPTIONWHEN OTHERS THENINSERT INTO error_log (code, message, time)VALUES (SQLCODE, SQLERRM, SYSDATE);COMMIT;RAISE; -- 继续向上层传递异常
END;
2. 动态错误消息
RAISE_APPLICATION_ERROR(-20001, '订单 ' || order_id || ' 状态无效');
3. 异常传播控制
BEGIN-- 业务逻辑
EXCEPTIONWHEN OTHERS THENIF SQLCODE = -2290 THENDBMS_OUTPUT.PUT_LINE('约束违反');ELSERAISE; -- 继续传播未明确处理的异常END IF;
END;
五、总结
优先级处理:
EXCEPTIONWHEN NO_DATA_FOUND THEN ... -- 特定异常在前WHEN OTHERS THEN ... -- 通用处理在后
事务控制:
BEGINSAVEPOINT sp1;-- DML操作
EXCEPTIONWHEN OTHERS THENROLLBACK TO sp1;RAISE;
END;
错误代码规范:
- 自定义错误代码范围:-20000 到 -20999
- 统一错误消息格式
通过合理使用异常处理机制,可显著提升Oracle程序的健壮性。建议在复杂业务逻辑中优先定义清晰的异常处理策略。