异常的定义、抛出和处理

何错误都叫异常,不期而遇时,执行单元必须交割控制权,这是一个肉包子打狗--有去无回的过程
PL/SQL引擎对系统异常、用户异常或者应用异常皆是一视同仁
异常仅是异常吗?有些异常如NO_DATA_FOUND,我们更愿意待他是逻辑的一个分支
下面详细介绍异常的定义、抛出和处理
㈠ 定义异常

隐姓埋名的异常仍是合法公民,但他们是可读性差、维护成本高的主要贡献者
异常,必也正名乎!
语法:
必须在声明部分
exception_name EXCEPTION;
用法:
① 执行单元的RAISE语句
RAISE exception_name;
② 异常处理单元的WHEN语句
WHEN exception_name THEN;
然而,当我们不得不和匿名异常打交道时,比如:
① 神秘的世外高人--匿名的系统异常
② RAISE_APPLICATION_ERROR抛出的应用异常
这时,神器"EXCEPTION_INIT",江湖人称--编译命令,可助你为匿名异常正名。
语法:
必须在声明部分
exception_name EXCEPTION;--该异常已经在同一个块或外层块、或者包的规范部分被命名
PRAGMA EXCEPTION_INIT(exception_name,error_number);
注释:
--error_number的约束
● 不可使用-1403(是NO_DATA_FOUND的错误号之一)
● 不可使用0或100之外的任何正数
● 不可使用<-1000000的负数
--最佳实践
建议把EXCEPTION_INIT集中到一个包,比如:
CREATE OR REPLACE PACKAGE pkg_think
IS
invalid_date EXCEPTION;
PRAGMA EXCEPTION_INIT(invalid_date,-110);
END pkg_think;
我们就可以在任何程序中这些异常,比如:
WHEN pkg_think.invalid_date THEN ...
对于异常的作用范围,Think有必要唠叨两下
我们知道程序于外部代码都是透明的,是个"黑盒子"
比如,我们可以在A过程抛出B异常,但是不能从调用A过程的程序C中抛出B异常

㈡ 抛出异常

手工抛出异常的法子有二:
▼ RAISE语句
▼ RAISE_APPLICATION_ERROR过程
先看RAISE
语法:
RAISE exception_name; OR
RAISE package_name.exception_name; OR
RAISE;
注释:
方式一,可用于抛出一个在当前块或包含当前块的外层块的自定义的异常
也可抛出STANDARD包预定义的系统异常
方式三,只可用在异常处理单元的WHEN语句,常用于异常的传播
再瞧RAISE_APPLICATION_ERROR
相比RAISE,此君的优点在于,可以给异常加注解
语法:
RAISE_APPLICATION_ERROR(error_number,error_comments);
这里的error_number只能介于-20999~-20000这1000个错误号

㈢ 处理异常

有两个术语扯下先:
▲ 异常处理单元:EXCEPTION关键字指示异常处理单元的开始
▲ 异常处理句柄:每个独立的WHEN...THEN...都是一个句柄
一个异常处理单元可以有多个处理句柄,比如:
EXCEPTION
/*句柄一*/
WHEN NO_DATA_FOUND
THEN ...
/*句柄二*/
WHEN payment_overdue
THEN ...
/*句柄三*/
WHEN OTHERS
THEN ...
END;
WHEN语句只能根据异常的名称而无法依照错误号来捕获异常
WHEN OTHERS语句是可选,而且必须是异常处理单元的最后一个处理句柄,如果没有这个语句,任何未处理异常立即传播到外层块
在异常处理单元里包含WHEN OTHERS语句,可以捕获所有其他的未处理的异常
WHEN OTHERS 和SQLCODE的结合使用可以替代EXCEPTION_INIT的功能
不过要仔细WHEN OTHERS的使用,因为它很容易"吞掉"异常,向外层和用户隐瞒这些异常
当然啦,我们通过OR操作符在一个单独的句柄中包含多个异常,无论这个异常来自系统还是应用
然而,不可用AND操作符,因为任意时刻只能有一个异常抛出
在进一步讨论异常处理细节前,先看两个内置的异常处理函数
▲ SQLCODE:返回代码中最后一次抛出的错误号
▲ SQLERRM:返回某个错误号所对应的错误消息
还有就是作用在异常身上的两个动作:
--抛出:由执行单元→→异常处理单元(一般在同层块)
--传播:由内层块→→外层块
所有程序员自定义的异常的错误号都是1,错误消息都是"User Defined Exception"
除非使用EXCEPTION_INIT为这个声明的异常指定不同错误号,以及RAISE_APPLICATION_ERROR给它指定不同的错误消息
如果你使用了局部定义异常,就应该提供一个针对该错误名的异常句柄
否则,未处理的异常传播到外层块,外层块皆视若无睹,很不上心呀
开篇Think提到了,一旦交出控制权,就意味着彻底没有"皇帝轮流坐,明年到我家"的机会

那么如何"屌丝逆袭"呢?

在执行单元抛出的异常总是在当前块中被处理--如果有匹配的句柄存在
你可以通过给任何语句加上一个BEGIN,之后加上一个EXCEPTION单元和END语句构建一个"虚拟块"
用这种方法,你可以通过在代码中构建匿名块的方式来控制异常引起的失败范围
并且,Think还建议把所有要隔离的代码都放到单独的过程或者函数中去
这样,有一个关键的好处在于,你可以从程序主线上隐藏掉BEGIN-EXCEPTION-END语句
使程序更加容易阅读,理解,维护和在多种环境重复使用


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值