林秀丽 邹贵红 卢道设
摘要:编程领域无论是高手还是新手,程序编写过程中或业务逻辑处理中,遇到难以周全考虑的错误是很正常的,所以任何一个优秀程序都应该带有对异常的捕获、提示、处理并恢复的功能。目的提高程序的健壮性。该文以ORACLE数据库为例,对异常处理进行剖析。
关键词:ORACLE数据库;程序健壮性;异常处理
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2018)10-0003-03
Abstract: Either the master or novice programming, During the preparation process or business logic processing, It is difficult to take into account the error is normal, So any good programs should be with and on the recovery of abnormal, suggesting that capture, processing function,Objective to improve the robustness of the program。In this paper, the ORACLE database as an example, Analysis of exception handling.
Key words: ORACLE database; the robustness of the program; exception handling
程序编写中,错误总会遇到不少,错误一般分为两种,一种是输入错误,另一种是逻辑性错误[1]。ORACLE将错误进行了归纳,即:编译时刻错误;运行时刻错误(也称异常错误)。
1 异常处理分类
1.1 编译时刻错误
编译错误是因用户错误的拼写关键字、对象名以及错误的语法格式等造成,此类错误在编译时PL/SQL引擎会发并报告给用户,此时程序是处在运行前。
例1:查询中数据表名输入错,编译时,PL/SQL引擎立马发现错误并提示。
1.2 运行时刻错误(异常错误)
编译即便通过,错误在运行时刻还是有可能产生。运行时刻错误产生的原因有很多,例如:硬件故障、内存不足、表的完整性约束被违反、被零除、数据在大小长度上的不匹配等[2][3]。
编译时刻错误在程序内部没必要做特殊处理。对运行时刻错误,因运行环境的不确定性此错误可能会随时出现。程序员必须在写程序过程中,对潜在的异常错误要尽可能的考虑并做针对性的处理,目的提高程序的健壮性。
PL/SQL对运行时刻错误提供了自动捕获与处理机制。
例2:被零除错误产生的异常,在PL/SQL引擎编译可以通过,只在运行时刻错误才呈现。
2 异常错误处理
对程序中出现的异常错误要进行处理,异常处理结构是固定的,如下:
异常定义区对预定义异常是不需要进行定义的;异常执行是可以显式引发异常,也可以由PL/SQL引擎引发异常;若引发了异常,则引发异常的后续语句将停止执行,语句转向异常处理区,直到异常处理完成,再回来。
异常分为预定义异常(系统预定义异常,非预定义异常)以及用户定义异常。这两种异常有不同的定义和引发方式,但对处理异常错误的程序编写方法与执行过程是一样的。
无论是哪一类异常,ORACLE在引发异常时都有一个序号,但程序进行异常处理时,不直接使用异常序号,异常的引用与处理必须使用名字。
2.1 预定义异常
预定义异常分为两类:第一类是系统预定义异常,此类异常是系统定义的,提供了异常序号与异常名称,二十来个,可直接使用,但在异常的捕获与处理上太有限。第二类是非预定异常,此类异常系统只提供了异常产生时的序号,并没有提供异常名,在程序中要捕获这类异常,用户就必须自己声明异常的名称,用EXCEPTION_INT编译命令建立异常序号同异常名称的联系,当程序发生异常时,就会自动以该名称引发对应序号的异常错误。
例3:系统预定义异常。可用多条件选择,在CASE语句的WHEN条件部分,引发系统预定义的CASE_NOT_FOUNT异常错误,异常处理部分可直接使用异常错误名称对异常错误进行捕获。
例4:非预定义异常。在插入语句中出现插入异常错误,系统提供了异常错误序号(ORA-01400),但系统并没有给此序号以对应的名称,用户就必须自己声明名称(ept_null_error),然后同异常错误序号ORA-01400对应,这样程序就可以按异常名称ept_null_error捕获处理异常。
预定义异常还可以通过SQLCODE和SQLERRM内置函数来处理,但此两函数在SQL语句中均不能直接使用,需要先将它们赋值给变量后,才能在SQL语句中使用。
SQLCODE函数不带参数,返回的是ORACLE错误序号。SQLERRM函数参数可写可不写,若写参数则为错误序号,此函数带参数时返回其错误序号对应的错误消息文本,若省去参数此函数返回SQLCODE当前值对应的错误消息文本。
例5:SQLCODE返回的错误序号为-2292,SQLERRM返回的是错误序号-2292的错误消息文本“ORA-02292: 违反完整约束条件 (HR.COUNTR_REG_FK) - 已找到子记录”。
2.2 自定义异常
ORACLE能判断的异常错误且能提供异常错误序号的只有预定义异常,但在实际使用中远远不能满足异常处理的要求。程序员必须自定义一些异常,來满足具体业务规则以及程序的编程与调试需求。自定义异常往往不一定是什么错误,而是让程序的结构完美。如:利用异常处理部分对某些问题进行集中处理。
必须先声明自定义异常,且异常引发要用RAISE语句显示抛出,自定义异常没有异常序号。
对于自定义异常也可以使用RAISE_APPLICATION_ERROR过程来实现,此函数对异常的错误序号与异常的错误消息文本都可自定义,使用灵活方便。此过程很多时候是为应用程序编程风格提供方便,不一定非要用于异常错误的处理。
例6:建一个使用了RAISE_APPLICATION_ERROR的过程dept_mgr,此过程是判断一个部门是否有管理员的情况自定义异常错误代码“-20001”,“-20002”与对应消息文本“该部门编码超出了取值范围”,“该部门没有管理员”。仅接着调用dept_mgr过程,按部门编码与该部门有没有管理员来捕获异常错误。
3 异常传递
当异常被引发时,就会立马跳转到EXCEPTION的异常处理语句中查询是否有匹配的异常,若在当前语句块或者子语句块中都没有匹配的异常,那么此异常就会向当前语句块的外层或子语句块的调用方传递,直到搜索块终止还没有匹配的异常,此时就会向PL/SQL引擎抛出一个未处理的异常。
以下详细剖析异常的传递机制[4][5]。
3.1 异常传递引发于执行部分
在当前块的执行部分引发异常的处理机制:若在当前块中有匹配的异常处理,则执行该异常处理,然后将控制权传到外层语句块;若在当前块中没有匹配的异常处理,则异常会被传到外层的异常处理部分查询匹配的异常处理;若异常一直被传递直到最外层语句块,都没查询到匹配的异常处理,则该程序将异常结束,且在被调用环境中显示错误信息。
如图2,异常1在当前块中有匹配的异常处理;异常2被传递到外层块中有匹配的异常处理对应;异常3是没有查询到匹配的异常处理的。
3.2 异常传递引发于声明部分
异常传递引发于声明部分,如变量在初始化是产生异常错误,此时异常会立马向外层块传递,而不被当前块的异常处理部分捕获。
如图3,A 变量定义长度为3,可赋值长度超过了3,结果引发异常错误,此异常错误是向外层块传递的。
3.3 异常传递引发于异常内部
异常传递引发于异常内部,是指异常在处理中也有引发异常的可能。异常内部引发异常,是会立马被传递到外层语句块,不论本块是否能进行异常处理。异常内部引发异常,可有RAISE显式引发,或者因某种错误由ORACLE检测到进行隐式引发。
如图4,异常2引发于异常1内部,结果被传递到外层块处理。
4 结论
本文对PL/SQL中的异常进行了剖析,从程序的健壮性阐述了异常的分类,对异常的处理机制分为两种,一种是预定义异常,一种是自定义异常,当预定义异常不能满足程序需要时,自定义异常可以派上用场。同时详细介绍了异常的不同传递。以便初学者深入理解異常处理机制,开发人员灵活使用异常机制解决问题。
参考文献:
[1] 李兴华,马云涛. Oracle开发经典[M].北京:清华大学出版社,2012(16):436-448.
[2] 龚永罡. Oracle 11g管理与应用实践教程[M].北京:清华大学出版社,2014(7).
[3] 路川,胡欣杰. Oracle 11g宝典[M].北京:电子工业出版社,2009(4):198-207.
[4] 谷长勇,吴逸云,单永红,陈杰. Oracle 11g权威指南[M]. 2版.北京:电子工业出版社,2011(18).
[5] 明昌科技. Oracle从入门到精通[M].北京:清华大学出版社,2012(14).