报警是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
在很多地方的作用也是无可替代,这里先不加以详细阐述。