報警是Oracle中單向的、以事務提交爲基礎的對數據庫報警事件的異步報警通知,比如可以在數據庫數據發生變化時,某個字段的值發生變化時,通過報警向用戶發送電子郵件通知。
報警是基於事務的,意味着除非能夠報警的事務被提交,否則等過過程中的事務不會獲得報警。
要使用DBMS_ALERT
包,需要以DBA身份登錄,爲用戶賦權:
GRANT EXECUTE ON DBMS_ALERT TO scott;
報警需要定義報警的發佈方和接收方,對應同一個會話中的兩個進程或者是不同會話中的進程。一個程序可以註冊多個命名的報警,然後使用WAIT和WAITANY等待其中的任何一個報警產生。
註冊報警事件REGISTER
在一個會話中可以註冊多個報警,語法如下:
DBMS_ALERT.REGISTER(name IN VARCHAR2);
等待特定報警WAITONE
該過程用於等待當前會話中特定的報警事件,並且在發生報警事件時輸出報警消息,該過程在執行之前會隱含地發出COMMIT,語法如下:
DBMS_ALERT.WAITONE(name IN VARCHAR2,
message OUT VARCHAR2,
status OUT INTEGER,
timeout IN NUMBER DEFAULT MAXWAIT);
name是等待報警的名稱,message用來返回報警信息,該信息是由SIGNAL發佈報警時發佈的,如果有多個此類報警在WAITONE過程執行前發生,那麼只返回最近的報警發佈信息,其他的將被丟棄。status在報警發生時會返回0,如果超時,返回1。timeout是等待報警發生時的時間,如果在此時間內無報警發生,則status返回1。
等待任意報警WAITANY
WAITANY過程會等待會話中已經註冊的任何報警的發生,在這個過程執行前會有一個隱含的COMMIT被執行,同樣,在等待這個報警發生的同事還可以發佈某些報警。語法如下:
DBMS_ALERT.WAITANY(name OUT VARCHAR2,
message OUT VARCHAR2,
status OUT INTEGER,
timeout IN NUMBER DEFAULT MAXWAIT);
參數大部分與WAITONE相同,只是name參數爲輸出參數,用來返回等待報警的名稱。
刪除報警REMOVE
DBMS_ALERT.REMOVE(name IN VARCHAR2);
刪除所有報警REMOVEALL
這個過程會刪除會話中所有註冊表中的報警,該過程會在一個會話開始時被自動調用,總是執行COMMIT。
DBMS_ALERT.REMOVEALL;
時間間隔設置SET_DEFAULTS
用於設置檢測報警時間的時間間隔,默認時間間隔爲5秒。
DBMS_ALERT.SET_DEFAULTS(sensitivity IN NUMBER);
設置報警消息SIGNAL
該過程用於指定報警時間所對應的報警消息,只有在提交事務是纔會發生報警信息,而當回退事務是不會發生報警信息的。
DBMS_ALERT.SIGNAL(name IN VARCHAR2, message VARCHAR2);
消息長度不能超過1800字節。
多個會話可以併發執行同一個報警,每個會話發佈報警時會阻塞其他會話發佈的報警,直到報警被提交,因此事務會以序列的方式發生。
爲了演示報警,是用兩個會話來進行測試。在第一個會話中創建如下的PL/SQL匿名塊,等待報警發生:
DECLARE
v_alertname VARCHAR2 (30) := 'alert_demo'; --報警名稱
v_status INTEGER; --等待狀態
v_msg VARCHAR2 (200); --報警消息
BEGIN
--註冊報警,指定報警名爲alert_demo
DBMS_ALERT.REGISTER (v_alertname);
--監聽報警,等待報警發生
DBMS_ALERT.waitone(v_alertname, v_msg, v_status);
--如果不返回0,表示報警示敗
IF v_status != 0
THEN
DBMS_OUTPUT.put_line ('error'); --顯示錯誤消息
END IF;
DBMS_OUTPUT.put_line (v_msg); --顯示報警消息
END;
可以看到,在代碼中註冊了一個名爲alert_demo
的報警,然後調用waitone等待報警,如果在SQL*Plus執行這段代碼,可以看到整個界面被阻塞了,等待有報警產生。下面的代碼將使用SIGNAL定義報警消息,並使用COMMIT語句發出報警:
DECLARE
v_alertname VARCHAR2 (30) := 'alert_demo';
BEGIN
--向報警alert_demo發送報警信息
DBMS_ALERT.signal (v_alertname, 'dbms_alert測試!');
COMMIT; --觸發報警,如果是ROLLBACK,則不觸發報警。
END;
可以看到,當這段匿名的語句塊一執行,報警的等待會話就會打印出在SIGNAL中指定的報警文本,並退出等待狀態。
可以看到報警這種機制可以實現多個會話交互,但是有許多侷限性,比如一旦開始等待,進程將不再做任何事情,因此是雙方難以交互。但是DBMS_ALERT
在很多地方的作用也是無可替代,這裏先不加以詳細闡述。