異常處理
?
當(dāng)異常生成之后,程序被中止,控制權(quán)交給異常處理模塊,異常處理模塊捕獲當(dāng)前異常句柄,并交由相應(yīng)的程序處理;如果,異常促里模塊沒有捕捉到異常句柄,那么它將被傳輸?shù)疆?dāng)前程序的外圍。
?
除非由一些特殊的要求,一般情況下異常將再當(dāng)前程序的異常處理模塊中被處理。異常處理模塊以
EXCEPTION
開始
END;
結(jié)尾。
?????? Declare
??????
?????? /*…………*/
?????? begin
??????
?????? /*…………*/
?????? exception
????????????? when /*
異常名稱
*/
????????????? then /*
異常處理
*/
????????????? when other
????????????? then /*
異常處理
*/
?????? end;
異常處理模塊的語(yǔ)法基本上以
CASE
一致,凡是在
when
中有定義的異常都將被處理,而沒有的則被傳輸。一個(gè)特殊的異常處理語(yǔ)句是
WHEN OTHERS
。就想在(
3
)中所說(shuō)的,它會(huì)處理所有為被處理的異常,因此必須小心使用它,最好是在最外層的程序中。當(dāng)然如果喜歡偷懶的,大可以在異常處理模塊中只放一個(gè)
OTHERS
。注意,無(wú)論哪種情況,
OTHERS
只能這只在異常處理的最后一位。
?
有趣的是,可以在一個(gè)
when
中處理多個(gè)異常句柄。
?????? Exception
????????????? When no_data_found or invalid_employee_id or dbms_ldap.invalid session
????????????? Then /*………..*/
?????? End;
?????? /
在這個(gè)例子里,有標(biāo)準(zhǔn)包的異常、自定義異常和非標(biāo)準(zhǔn)包中的異常。這些異常只能用
or
連接,不可以用
and
,因?yàn)橹挥幸粋€(gè)異常能夠生成。
?
非
raise_application_error
生成的異常,如果沒有被處理而一直傳遞到系統(tǒng)環(huán)境中,那么環(huán)境將視情況作出相應(yīng)的反映。在
sqlplus
中,
oracle
將回滾所有
DML
對(duì)數(shù)據(jù)所做的修改。在
sqlplus
環(huán)境中,因?yàn)橛凶詣?dòng)回滾的存在,我們可以保留出現(xiàn)未被處理的異常的可能性;而在另外的一些環(huán)境中,則需要仔細(xì)設(shè)計(jì)最外層程序。
ü????????
捕捉任何有可能傳出的異常。
ü????????
記錄錯(cuò)誤以便于分析。
ü????????
給外部環(huán)境一個(gè)信息,以便于其作出相應(yīng)的處理。
?
對(duì)于自定義異常,因?yàn)?/span>
sqlcode
值永遠(yuǎn)是
1
,所以當(dāng)它被傳出時(shí),如果外圍程序中沒有定義相同名稱的異常,我們將不知道是什么異常產(chǎn)生了。因此,不要將自定義異常傳遞出去。
?
在程序中處理幾個(gè)互相獨(dú)立的操作時(shí),為了避免出現(xiàn)因?yàn)橐粋€(gè)操作產(chǎn)生異常而使整個(gè)程序被中斷的情況,有必要將這些獨(dú)立的操作放在各自的虛擬塊中。
?????? Procedure change_data is
?????? Begin
????????????? Begin
?????????????
?????? Delete from employee where …..
??????
?????? Exception
?????????????
?????? When others then null;
????????????? End;
?
????????????? Begin
?????????????
?????? Update company set …….
??????
?????? Exception
When others then null;
????????????? End;
?
????????????? Begin
?????????????
?????? Insert into company_history select * from company where ….
??????
?????? Exception
?????????????
?????? When others then null;
????????????? End;
?????? End;
?????? /
?
?
Pl/sql
提供了一些內(nèi)建的函數(shù)來(lái)幫助我們確定、分析異常。
?
SQLCODE
這個(gè)函數(shù)在前面有提到過,它是一個(gè)用于返回當(dāng)前模塊中最近一次異常值的函數(shù),或者說(shuō)是非入棧程序的異常值。打個(gè)比方:如果在當(dāng)前程序的異常模塊中調(diào)用了另一個(gè)程序,
oracle
將當(dāng)前程序及相應(yīng)的環(huán)境變量(包括異常值)壓入系統(tǒng)棧;在被調(diào)用程序中生成了一個(gè)值為
1
的異常,那么
sqlcode
將返回
1
;之后剛才的程序出棧,
sqlcode
返回當(dāng)前異常值。需要注意的是,不要在異常模塊之外使用它,這樣不會(huì)有任何意義。當(dāng)沒有異常或在異常模塊之外使用時(shí),
SQLCODE
返回
0
;返回值
1
是指自定義異常。
?
SQLERRM
接收異常值,返回相應(yīng)的長(zhǎng)度不超過
512
字節(jié)的描述語(yǔ)。如果沒有傳入異常值,則返回當(dāng)前異常描述。
?????? Begin
??????
?????? Dbms_output.put_line( sqlerrm(-1403);
?????? End;
Sql>/
Ora-1403: no data found
在需要體構(gòu)長(zhǎng)度超過
512
字節(jié)的描述時(shí),
oracle
建議使用
dbms_utility.format_error_stack
。顯然,用這個(gè)函數(shù)來(lái)判斷一個(gè)異常是否為系統(tǒng)異常是很有用的,如果不是的話,將返回以下兩種情況的一種。
如果是一個(gè)負(fù)數(shù):
?????? ora-nnnnn: message not found,; product=rdbms; facility=ora
如果是一個(gè)正數(shù):
?????? -nnnnn: non-oracle exception
?
DBMS_UTILITY.FORMAT_ERROR_STACK
返回當(dāng)前異常相應(yīng)的描述,沒有字符長(zhǎng)度限制。與
SQLCODE
相同的是,必須在異常處理模塊中使用。雖然名稱中有一個(gè)
stack
在,但通過它并不能知道異常的最初生成處,需要的話就必須使用
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
。
?
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
系統(tǒng)為最近一次生成的異常設(shè)置了一個(gè)棧,并跟蹤它的傳遞過程,而這個(gè)函數(shù)使用這個(gè)棧,然后返回該異常的整個(gè)傳遞過程。這個(gè)函數(shù)對(duì)錯(cuò)誤的定位和實(shí)施下一步處理起著至關(guān)重要的作用。
?????? Create or replace procedure procl is
?????? Begin
??????
?????? Dbms_output.put_line(‘running proc1’);
????????????? Raise no_data_found;
?????? End;
?????? /
?????? create or replace procedure proc2 is
?????? begin
??????
?????? dbms_output.put_line(‘calling proc1’);
????????????? proc1;
?????? end;
?????? /
?????? create or replace procedure proc3 is
?????? begin
??????
?????? dbms_output.put_line(‘calling proc2’);
????????????? proc2;
?????? exception
????????????? when no_data_found
????????????? then
?????????????
?????? dbms_output.put_line(‘error stack at top level’);
?????????????
?????? dbms_output.put_line(dbms_utility.format_error_backtrace);
?????? end;
?????? /
現(xiàn)在可以運(yùn)行
proc3
來(lái)看看結(jié)果。
Sql>set serveroutput on;
Sql>begin
2????????????????????????
dbms_output.put_line(‘proc3->proc2->proc1 backtrace’);
3????????????????????????
proc3;
4???? end;
5???? /
??? Proc3 -> Proc2 -> Proc1 backtrace
??? calling proc2
??? calling proc1
??? running proc1
??? Error stack at top level:
??? ORA-06512: at "SCOTT.PROC1", line 4
??? ORA-06512: at "SCOTT.PROC2", line 5
ORA-06512: at "SCOTT.PROC3", line 4
事實(shí)上,每次異常的產(chǎn)生都將重置這個(gè)異常棧,只是最后一次從系統(tǒng)棧出棧的是最外層的程序塊,所以可以清楚地看到異常生成的整個(gè)過程。上面這個(gè)程序的執(zhí)行過程是這樣的:首先用
put_line
打印
Proc3 -> Proc2 -> Proc1 backtrace
,
調(diào)用
proc3
,當(dāng)前程序入棧
=>
打印
calling proc2
,調(diào)用
proc2
,
proc3
入棧
=>
打印
calling proc1
,調(diào)用
proc1
,
proc2
入棧
=>
打印
running proc1
,生成
no_data_found
異常,該異常被壓入異常棧中
=>? proc2
出棧,并檢測(cè)到來(lái)自第
5
行調(diào)用傳遞過來(lái)的異常,將它在此壓入異常棧
=> proc3
出棧,并檢測(cè)到來(lái)自第
4
行調(diào)用傳遞過來(lái)的異常,將它在此壓入異常棧,
dbms_utility.format_error_backtrace
將異常棧中信息反相打印出來(lái)
=>?
最外層程序出棧,
end
。
以下是正確使用這個(gè)函數(shù)的一些注意事項(xiàng):
ü????????
在當(dāng)前程序的異常處理模塊中調(diào)用這個(gè)函數(shù)。
ü????????
避免在中間程序中使用異常處理模塊。
這樣異常就能被正確地傳輸?shù)阶钔鈱映绦蛑校⒋蛴〕鲞@個(gè)過程了。