|
“數(shù)據(jù)庫(kù)更改通知”入門(mén)
“數(shù)據(jù)庫(kù)更改通知”特性的用法非常簡(jiǎn)單:創(chuàng)建一個(gè)針對(duì)通知執(zhí)行的通知處理程序 – 一個(gè) PL/SQL 存儲(chǔ)過(guò)程或客戶端 OCI 回調(diào)函數(shù)。然后,針對(duì)要接收其更改通知的數(shù)據(jù)庫(kù)對(duì)象注冊(cè)一個(gè)查詢,以便每當(dāng)事務(wù)更改其中的任何對(duì)象并提交時(shí)調(diào)用通知處理程序。通常情況下,通知處理程序?qū)⒈恍薷牡谋淼拿Q、所做更改的類型以及所更改行的行 ID(可選)發(fā)送給客戶端監(jiān)聽(tīng)程序,以便客戶端應(yīng)用程序可以在響應(yīng)中執(zhí)行相應(yīng)的處理。
為了了解“數(shù)據(jù)庫(kù)更改通知”特性的作用方式,請(qǐng)考慮以下示例。假設(shè)您的 php 應(yīng)用程序訪問(wèn) OE.ORDERS 表中存儲(chǔ)的訂單以及 OE.ORDER_ITEMS 中存儲(chǔ)的訂單項(xiàng)。鑒于很少更改已下訂單的信息,您可能希望應(yīng)用程序同時(shí)緩存針對(duì) ORDERS 和 ORDER_ITEMS 表的查詢結(jié)果集。要避免訪問(wèn)過(guò)期數(shù)據(jù),您可以使用“數(shù)據(jù)庫(kù)更改通知”,它可讓您的應(yīng)用程序方便地獲知以上兩個(gè)表中所存儲(chǔ)數(shù)據(jù)的更改。
您必須先將 CHANGE NOTIFICATION 系統(tǒng)權(quán)限以及 EXECUTE ON DBMS_CHANGENOTIFICATION 權(quán)限授予 OE 用戶,才能注冊(cè)對(duì) ORDERS 和 ORDER_ITEMS 表的查詢,以便接收通知和響應(yīng)對(duì)這兩個(gè)表所做的 DML 或 DDL 更改。為此,可以從 SQL 命令行工具(如 SQL*Plus)中執(zhí)行下列命令。
CONNECT / AS SYSDBA;
GRANT CHANGE NOTIFICATION TO oe;
GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO oe;
確保將 init.ora 參數(shù) job_queue_processes 設(shè)置為非零值,以便接收 PL/SQL 通知。或者,您也可以使用下面的 ALTER SYSTEM 命令:
ALTER SYSTEM SET "job_queue_processes"=2; 然后,在以 OE/OE 連接后,您可以創(chuàng)建一個(gè)通知處理程序。但首先,您必須創(chuàng)建將由通知處理程序使用的數(shù)據(jù)庫(kù)對(duì)象。例如,您可能需要?jiǎng)?chuàng)建一個(gè)或多個(gè)數(shù)據(jù)庫(kù)表,以便通知處理程序?qū)⒆?cè)表的更改記錄到其中。在以下示例中,您將創(chuàng)建 nfresults 表來(lái)記錄以下信息:更改發(fā)生的日期和時(shí)間、被修改的表的名稱以及一個(gè)消息(說(shuō)明通知處理程序是否成功地將通知消息發(fā)送給客戶端)。
CONNECT oe/oe;
CREATE TABLE nfresults (
operdate DATE,
tblname VARCHAR2(60),
rslt_msg VARCHAR2(100)
);
在實(shí)際情況中,您可能需要?jiǎng)?chuàng)建更多表來(lái)記錄通知事件以及所更改行的行 ID 等信息,但就本文而言,nfresults 表完全可以滿足需要。
使用 UTL_HTTP 向客戶端發(fā)送通知
您可能還要?jiǎng)?chuàng)建一個(gè)或多個(gè) PL/SQL 存儲(chǔ)過(guò)程,并從通知處理程序中調(diào)用這些存儲(chǔ)過(guò)程,從而實(shí)現(xiàn)一個(gè)更具可維護(hù)性和靈活性的解決方案。例如,您可能要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn)將通知消息發(fā)送給客戶端的存儲(chǔ)過(guò)程。“清單 1”是 PL/SQL 過(guò)程 sendNotification。該過(guò)程使用 UTL_HTTPPL 程序包向客戶端應(yīng)用程序發(fā)送更改通知。
清單 1. 使用 UTL_HTTP 向客戶端發(fā)送通知
復(fù)制代碼 代碼如下:
CREATE OR REPLACE PROCEDURE sendNotification(url IN VARCHAR2,
tblname IN VARCHAR2, order_id IN VARCHAR2) IS
req UTL_HTTP.REQ;
resp UTL_HTTP.RESP;
err_msg VARCHAR2(100);
tbl VARCHAR(60);
BEGIN
tbl:=SUBSTR(tblname, INSTR(tblname, '.', 1, 1)+1, 60);
BEGIN
req := UTL_HTTP.BEGIN_REQUEST(url||order_id||'&'||'table='||tbl);
resp := UTL_HTTP.GET_RESPONSE(req);
INSERT INTO nfresults VALUES(SYSDATE, tblname, resp.reason_phrase);
UTL_HTTP.END_RESPONSE(resp);
EXCEPTION WHEN OTHERS THEN
err_msg := SUBSTR(SQLERRM, 1, 100);
INSERT INTO nfresults VALUES(SYSDATE, tblname, err_msg);
END;
COMMIT;
END;
/
如“清單 1”所示,sendNotification 以 UTL_HTTP.BEGIN_REQUEST 函數(shù)發(fā)出的 HTTP 請(qǐng)求的形式向客戶端發(fā)送通知消息。此 URL 包含 ORDERS 表中已更改行的 order_id。然后,它使用 UTL_HTTP.GET_RESPONSE 獲取客戶端發(fā)出的響應(yīng)信息。實(shí)際上,sendNotification 并不需要處理客戶端返回的整個(gè)響應(yīng),而是只獲取一個(gè)在 RESP 記錄的 reason_phrase 字段中存儲(chǔ)的簡(jiǎn)短消息(描述狀態(tài)代碼)。
創(chuàng)建通知處理程序
現(xiàn)在,您可以創(chuàng)建一個(gè)通知處理程序,它將借助于上面介紹的 sendNotification 過(guò)程向客戶端發(fā)送更改通知。來(lái)看一看“清單 2”中的 PL/SQL 過(guò)程 orders_nf_callback。
清單 2. 處理對(duì) OE.ORDERS 表所做更改的通知的通知處理程序
復(fù)制代碼 代碼如下:
CREATE OR REPLACE PROCEDURE orders_nf_callback (ntfnds IN SYS.CHNF$_DESC) IS
tblname VARCHAR2(60);
numtables NUMBER;
event_type NUMBER;
row_id VARCHAR2(20);
numrows NUMBER;
ord_id VARCHAR2(12);
url VARCHAR2(256) := 'http://webserverhost/phpcache/dropResults.php?order_no=';
BEGIN
event_type := ntfnds.event_type;
numtables := ntfnds.numtables;
IF (event_type = DBMS_CHANGE_NOTIFICATION.EVENT_OBJCHANGE) THEN
FOR i IN 1..numtables LOOP
tblname := ntfnds.table_desc_array(i).table_name;
IF (bitand(ntfnds.table_desc_array(i).opflags,
DBMS_CHANGE_NOTIFICATION.ALL_ROWS) = 0) THEN
numrows := ntfnds.table_desc_array(i).numrows;
ELSE
numrows :=0;
END IF;
IF (tblname = 'OE.ORDERS') THEN
FOR j IN 1..numrows LOOP
row_id := ntfnds.table_desc_array(i).row_desc_array(j).row_id;
SELECT order_id INTO ord_id FROM orders WHERE rowid = row_id;
sendNotification(url, tblname, ord_id);
END LOOP;
END IF;
END LOOP;
END IF;
COMMIT;
END;
/
如“清單 2”所示,此通知處理程序?qū)?SYS.CHNF$_DESC 對(duì)象用作參數(shù),然后使用它的屬性獲取該更改的詳細(xì)信息。在該示例中,此通知處理程序?qū)⒅惶幚頂?shù)據(jù)庫(kù)為響應(yīng)對(duì)注冊(cè)對(duì)象進(jìn)行的 DML 或 DDL 更改(也就是說(shuō),僅當(dāng)通知類型為 EVENT_OBJCHANGE 時(shí))而發(fā)布的通知,并忽略有關(guān)其他數(shù)據(jù)庫(kù)事件(如實(shí)例啟動(dòng)或?qū)嵗P(guān)閉)的通知。從以上版本開(kāi)始,處理程序可以處理針對(duì) OE.ORDERS 表中每個(gè)受影響的行發(fā)出的更改通知。在本文后面的“將表添加到現(xiàn)有注冊(cè)”部分中,您將向處理程序中添加幾行代碼,以便它可以處理針對(duì) OE.ORDER_ITEMS 表中被修改的行發(fā)出的通知。
為更改通知?jiǎng)?chuàng)建注冊(cè)
創(chuàng)建通知處理程序后,必須為其創(chuàng)建一個(gè)查詢注冊(cè)。對(duì)于本示例而言,您必須在注冊(cè)過(guò)程中對(duì) OE.ORDER 表執(zhí)行查詢并將 orders_nf_callback 指定為通知處理程序。您還需要在 DBMS_CHANGE_NOTIFICATION 程序包中指定 QOS_ROWIDS 選項(xiàng),以便在通知消息中啟用 ROWID 級(jí)別的粒度。“清單 3”是一個(gè) PL/SQL 塊,它為 orders_nf_callback 通知處理程序創(chuàng)建查詢注冊(cè)。
清單 3. 為通知處理程序創(chuàng)建查詢注冊(cè)
復(fù)制代碼 代碼如下:
DECLARE
REGDS SYS.CHNF$_REG_INFO;
regid NUMBER;
ord_id NUMBER;
qosflags NUMBER;
BEGIN
qosflags := DBMS_CHANGE_NOTIFICATION.QOS_RELIABLE +
DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS;
REGDS := SYS.CHNF$_REG_INFO ('orders_nf_callback', qosflags, 0,0,0);
regid := DBMS_CHANGE_NOTIFICATION.NEW_REG_START (REGDS);
SELECT order_id INTO ord_id FROM orders WHERE ROWNUM<2;
DBMS_CHANGE_NOTIFICATION.REG_END;
END;
/
本示例針對(duì) ORDERS 表創(chuàng)建了一個(gè)注冊(cè),并將 orders_nf_callback 用作通知處理程序。現(xiàn)在,如果您使用 DML 或 DDL 語(yǔ)句修改 ORDERS 表并提交事務(wù),則將自動(dòng)調(diào)用 orders_nf_callback 函數(shù)。例如,您可能針對(duì) ORDERS 表執(zhí)行下列 UPDATE 語(yǔ)句并提交該事務(wù):
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2421;
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2422;
COMMIT;
要確保數(shù)據(jù)庫(kù)發(fā)布了通知來(lái)響應(yīng)以上事務(wù),您可以檢查 nfresults 表:
SELECT TO_CHAR(operdate, 'dd-mon-yy hh:mi:ss') operdate,
tblname, rslt_msg FROM nfresults;
結(jié)果應(yīng)如下所示:
OPERDATE TBLNAME RSLT_MSG
--------------------- ----------- ---------
02-mar-06 04:31:28 OE.ORDERS Not Found
02-mar-06 04:31:29 OE.ORDERS Not Found
從以上結(jié)果中可以清楚地看到,orders_nf_callback 已經(jīng)正常工作,但未找到客戶端腳本。在該示例中出現(xiàn)這種情況并不意外,這是因?yàn)槟⑽磩?chuàng)建 URL 中指定的 dropResults.php 腳本。
將表添加到現(xiàn)有注冊(cè)
前一部分介紹了如何使用更改通知服務(wù)使數(shù)據(jù)庫(kù)在注冊(cè)對(duì)象(在以上示例中為 ORDERS 表)發(fā)生更改時(shí)發(fā)出通知。但從性能角度而言,客戶端應(yīng)用程序可能更希望緩存 ORDER_ITEMS 表而非 ORDERS 表本身的查詢結(jié)果集,這是因?yàn)樗诿看卧L問(wèn)訂單時(shí),不得不從 ORDERS 表中只檢索一行,但同時(shí)必須從 ORDER_ITEMS 表中檢索多個(gè)行。在實(shí)際情況中,訂單可能包含數(shù)十個(gè)甚至數(shù)百個(gè)訂單項(xiàng)。
由于您已經(jīng)對(duì) ORDERS 表注冊(cè)了查詢,因此不必再創(chuàng)建一個(gè)注冊(cè)來(lái)注冊(cè)對(duì) ORDER_ITEMS 表的查詢了。相反,您可以使用現(xiàn)有注冊(cè)。為此,您首先需要檢索現(xiàn)有注冊(cè)的 ID。可以執(zhí)行以下查詢來(lái)完成此工作:
SELECT regid, table_name FROM user_change_notification_regs; 結(jié)果可能如下所示:
REGID TABLE_NAME
----- --------------
241 OE.ORDERS
獲取注冊(cè) ID 后,可以使用 DBMS_CHANGE_NOTIFICATION.ENABLE_REG 函數(shù)將一個(gè)新對(duì)象添加到該注冊(cè),如下所示:
復(fù)制代碼 代碼如下:
DECLARE
ord_id NUMBER;
BEGIN
DBMS_CHANGE_NOTIFICATION.ENABLE_REG(241);
SELECT order_id INTO ord_id FROM order_items WHERE ROWNUM < 2;
DBMS_CHANGE_NOTIFICATION.REG_END;
END;
完成了!從現(xiàn)在開(kāi)始,數(shù)據(jù)庫(kù)將生成一個(gè)通知來(lái)響應(yīng)對(duì) ORDERS 和 ORDER_ITEMS 所做的任何更改,并調(diào)用 orders_nf_callback 過(guò)程來(lái)處理通知。因此,下一步就是編輯 orders_nf_callback,以便它可以處理因?qū)?ORDER_ITEMS 表執(zhí)行 DML 操作而生成的通知。但在重新創(chuàng)建 orders_nf_callback 過(guò)程之前,您需要?jiǎng)?chuàng)建以下將在更新過(guò)程中引用的表類型:
CREATE TYPE rdesc_tab AS TABLE OF SYS.CHNF$_RDESC; 然后,返回清單,在以下代碼行之后:
復(fù)制代碼 代碼如下:
IF (tblname = 'OE.ORDERS') THEN
FOR j IN 1..numrows LOOP
row_id := ntfnds.table_desc_array(i).row_desc_array(j).row_id;
SELECT order_id INTO ord_id FROM orders WHERE rowid = row_id;
sendNotification(url, tblname, ord_id);
END LOOP;
END IF;
插入以下代碼:
復(fù)制代碼 代碼如下:
IF (tblname = 'OE.ORDER_ITEMS') THEN
FOR rec IN (SELECT DISTINCT(o.order_id) o_id FROM
TABLE(CAST(ntfnds.table_desc_array(i).row_desc_array AS rdesc_tab)) t,
orders o, order_items d WHERE t.row_id = d.rowid AND d.order_id=o.order_id)
LOOP
sendNotification(url, tblname, rec.o_id);
END LOOP;
END IF;
重新創(chuàng)建 orders_nf_callback 后,您需要測(cè)試它能否正常工作。為此,您可以針對(duì) ORDER_ITEMS 表執(zhí)行下列 UPDATE 語(yǔ)句并提交該事務(wù):
UPDATE ORDER_ITEMS SET quantity = 160 WHERE order_id=2421 AND line_item_id=1;
UPDATE ORDER_ITEMS SET quantity = 160 WHERE order_id=2421 AND line_item_id=2;
COMMIT;
然后,檢查 nfresults 表,如下所示:
SELECT TO_CHAR(operdate, 'dd-mon-yy hh:mi:ss') operdate,
rslt_msg FROM nfresults WHERE tblname = 'OE.ORDER_ITEMS'; 輸出可能如下所示:
OPERDATE RSLT_MSG
------------------- --------------
03-mar-06 12:32:27 Not Found
您可能很奇怪為什么只向 nfresults 表中插入了一行 主站蜘蛛池模板: 九寨沟县| 双桥区| 宁河县| 安阳县| 交口县| 海林市| 宜良县| 陆良县| 西宁市| 固始县| 繁昌县| 体育| 逊克县| 都安| 安龙县| 武强县| 桃园县| 华池县| 郯城县| 昌宁县| 长宁区| 宜丰县| 永川市| 靖州| 汪清县| 丹江口市| 亚东县| 读书| 紫云| 铁力市| 顺平县| 神农架林区| 临夏市| 蕉岭县| 霍州市| 张北县| 彰武县| 安福县| 册亨县| 连平县| 曲阜市|