異常處理
?
當異常生成之后,程序被中止,控制權交給異常處理模塊,異常處理模塊捕獲當前異常句柄,并交由相應的程序處理;如果,異常促里模塊沒有捕捉到異常句柄,那么它將被傳輸到當前程序的外圍。
?
除非由一些特殊的要求,一般情況下異常將再當前程序的異常處理模塊中被處理。異常處理模塊以 EXCEPTION 開始 END; 結尾。
?????? Declare
?????? ?????? /*…………*/
?????? begin
?????? ?????? /*…………*/
?????? exception
????????????? when /* 異常名稱 */
????????????? then /* 異常處理 */
????????????? when other
????????????? then /* 異常處理 */
?????? end;
異常處理模塊的語法基本上以 CASE 一致,凡是在 when 中有定義的異常都將被處理,而沒有的則被傳輸。一個特殊的異常處理語句是 WHEN OTHERS 。就想在( 3 )中所說的,它會處理所有為被處理的異常,因此必須小心使用它,最好是在最外層的程序中。當然如果喜歡偷懶的,大可以在異常處理模塊中只放一個 OTHERS 。注意,無論哪種情況, OTHERS 只能這只在異常處理的最后一位。
?
有趣的是,可以在一個 when 中處理多個異常句柄。
?????? Exception
????????????? When no_data_found or invalid_employee_id or dbms_ldap.invalid session
????????????? Then /*………..*/
?????? End;
?????? /
在這個例子里,有標準包的異常、自定義異常和非標準包中的異常。這些異常只能用 or 連接,不可以用 and ,因為只有一個異常能夠生成。
?
非 raise_application_error 生成的異常,如果沒有被處理而一直傳遞到系統環境中,那么環境將視情況作出相應的反映。在 sqlplus 中, oracle 將回滾所有 DML 對數據所做的修改。在 sqlplus 環境中,因為有自動回滾的存在,我們可以保留出現未被處理的異常的可能性;而在另外的一些環境中,則需要仔細設計最外層程序。
ü???????? 捕捉任何有可能傳出的異常。
ü???????? 記錄錯誤以便于分析。
ü???????? 給外部環境一個信息,以便于其作出相應的處理。
?
對于自定義異常,因為 sqlcode 值永遠是 1 ,所以當它被傳出時,如果外圍程序中沒有定義相同名稱的異常,我們將不知道是什么異常產生了。因此,不要將自定義異常傳遞出去。
?
在程序中處理幾個互相獨立的操作時,為了避免出現因為一個操作產生異常而使整個程序被中斷的情況,有必要將這些獨立的操作放在各自的虛擬塊中。
?????? 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
提供了一些內建的函數來幫助我們確定、分析異常。
?
SQLCODE
這個函數在前面有提到過,它是一個用于返回當前模塊中最近一次異常值的函數,或者說是非入棧程序的異常值。打個比方:如果在當前程序的異常模塊中調用了另一個程序, oracle 將當前程序及相應的環境變量(包括異常值)壓入系統棧;在被調用程序中生成了一個值為 1 的異常,那么 sqlcode 將返回 1 ;之后剛才的程序出棧, sqlcode 返回當前異常值。需要注意的是,不要在異常模塊之外使用它,這樣不會有任何意義。當沒有異?;蛟诋惓DK之外使用時, SQLCODE 返回 0 ;返回值 1 是指自定義異常。
?
SQLERRM
接收異常值,返回相應的長度不超過 512 字節的描述語。如果沒有傳入異常值,則返回當前異常描述。
?????? Begin
?????? ?????? Dbms_output.put_line( sqlerrm(-1403);
?????? End;
Sql>/
Ora-1403: no data found
在需要體構長度超過 512 字節的描述時, oracle 建議使用 dbms_utility.format_error_stack 。顯然,用這個函數來判斷一個異常是否為系統異常是很有用的,如果不是的話,將返回以下兩種情況的一種。
如果是一個負數:
?????? ora-nnnnn: message not found,; product=rdbms; facility=ora
如果是一個正數:
?????? -nnnnn: non-oracle exception
?
DBMS_UTILITY.FORMAT_ERROR_STACK
返回當前異常相應的描述,沒有字符長度限制。與 SQLCODE 相同的是,必須在異常處理模塊中使用。雖然名稱中有一個 stack 在,但通過它并不能知道異常的最初生成處,需要的話就必須使用 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE 。
?
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
系統為最近一次生成的異常設置了一個棧,并跟蹤它的傳遞過程,而這個函數使用這個棧,然后返回該異常的整個傳遞過程。這個函數對錯誤的定位和實施下一步處理起著至關重要的作用。
?????? 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;
?????? /
現在可以運行 proc3 來看看結果。
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
事實上,每次異常的產生都將重置這個異常棧,只是最后一次從系統棧出棧的是最外層的程序塊,所以可以清楚地看到異常生成的整個過程。上面這個程序的執行過程是這樣的:首先用 put_line 打印 Proc3 -> Proc2 -> Proc1 backtrace , 調用 proc3 ,當前程序入棧 => 打印 calling proc2 ,調用 proc2 , proc3 入棧 => 打印 calling proc1 ,調用 proc1 , proc2 入棧 => 打印 running proc1 ,生成 no_data_found 異常,該異常被壓入異常棧中 =>? proc2 出棧,并檢測到來自第 5 行調用傳遞過來的異常,將它在此壓入異常棧 => proc3 出棧,并檢測到來自第 4 行調用傳遞過來的異常,將它在此壓入異常棧, dbms_utility.format_error_backtrace 將異常棧中信息反相打印出來 =>? 最外層程序出棧, end 。
以下是正確使用這個函數的一些注意事項:
ü???????? 在當前程序的異常處理模塊中調用這個函數。
ü???????? 避免在中間程序中使用異常處理模塊。
這樣異常就能被正確地傳輸到最外層程序中,并打印出這個過程了。