我會在apex.oracle.com上設置演示,但由於您需要在dbms_alert上執行授權,因此它必須僅爲純文本格式。
你可以在整個設置中走得相當遠,所以我認爲這是建立在基礎上的基礎。例如,我只使用了一個警報。在您的示例中,您可能想要使用多個事件來捕獲不同的進度警報。這是爲了簡單的原因,爲了將某些東西返回給客戶端(ajax響應),ajax回調函數必須「關閉」。因此,在捕獲警報並想要返回時,需要寫入緩衝區並將其返回。這意味着你也將停止聽這個事件(閱讀:在頂點,你應該!)。考慮流程如下:你將做一個ajax調用,並且有一個ajax回調過程來註冊一個事件的興趣。然後您等待警報發生。你可以通過將它寫入http緩衝區(htp.p
)來捕獲它並將其返回。這是代碼的結束,apex將刷新緩衝區,然後ajax調用會接收到響應,並且您將能夠管理該返回。
不要忘記:apex使用連接池,而數據庫會話不是直接鏈接,而是始終重用。你不想'離開'數據庫會話'變髒'。您也必須取消註冊您的警報興趣。這也使得對警報使用唯一ID的情況 - 警報可以在不同的(數據庫)會話中註冊,所以如果這是多個用戶可以用來跟蹤其進程的進度的頁面,則不需要希望他們干預其他用戶的提醒。
但是,這種短暫的興趣也意味着在不同的Ajax調用之間會有「中斷」。當您想要聽取多個警報時,這些警報可能會非常緊密地包裝在一起,因此您可能會錯過其中一個。假設2條警報間隔爲1ms:第一條警報將被捕獲,並報告給ajax呼叫,這將不得不立即開始新的呼叫,以便偵聽更多警報。但是由於在短時間內沒有積極的傾聽者,下一次提醒可能會被錯過。現在 - 這可能只是您在相同處理程序下觸發多個警報的問題。如果您使用多個處理程序並同時爲所有這些處理程序啓動ajax調用,那麼它們都會及時處理。當然,兩者都有解決方案。我想如果只使用一個處理程序,您可以捕獲集合中的所有警報,並檢查是否已發送特定警報的響應以及是否繼續檢查。使用多個處理程序,您可以使用唯一的ID並將其後綴爲不同的狀態。
所以這裏是我在本地POC中使用的一些實際代碼。
概述:我有3個按鈕:1來生成警報ID,爲此我使用了一個序列。另一個按鈕開始偵聽事件,另一個按鈕發送警報。對於NEW_ALERT_ID按鈕
JS代碼:
apex.server.process("NEW_ALERT").done(function(pdata){
$s("P1_ALERT_ID",pdata.alertId);
})
爲START_LISTEN按鈕
JS代碼:
apex.server.process("LISTEN_ALERT",{x01:$v("P1_ALERT_ID")},{timeout:(31*1000)})
.done(function(pdata){
if (pdata.success){
alert('Caught alert: ' + pdata.message);
} else {
alert("No alerts caught during wait on database. You may want to continue listening in...")
}
})
.fail(function(jqXHR, textStatus){
if(textStatus === 'timeout')
{
alert('Call should have returned by now...');
//do something. Try again perhaps?
}
});
爲SEND_ALERT按鈕
JS代碼:
apex.server.process("SEND_ALERT",{x01:$v("P1_ALERT_ID")},{dataType:"text"});
AJAX回調過程:
NEW_ALERT:
htp.p('{"alertId":'||alert_seq.nextval()||'}');
LISTEN_ALERT:
declare
alert_id number := apex_application.g_x01;
msg varchar2(2000);
stat pls_integer;
keep_looping boolean := true;
insurance binary_integer := 0; -- prevent an infinite loop
onecycle binary_integer := 3; -- one cycle of waiting, in seconds
maxcycles binary_integer := 10; -- in this session, the max amount of cycles to wait
begin
dbms_alert.register(alert_id);
while keep_looping
loop
insurance := insurance + 1;
dbms_alert.waitone(alert_id, msg, stat, onecycle);
if stat = 1 then
apex_debug.message('timeout occured, going again');
else
apex_debug.message('alert: '||msg);
keep_looping := false;
end if;
exit when insurance = maxcycles;
end loop;
if keep_looping then
-- we waited a really long time now. It may be a good idea to return this info to the client and let it start a new call
htp.p('{"success":false,"message":"No alert during wait on database"}');
else
htp.p('{"success":true,"message":"'||msg||'"}');
end if;
end;
SEND_ALERT:
declare
alert_id number := apex_application.g_x01;
begin
dbms_alert.signal(alert_id, 'alert sent at '||to_char(systimestamp, 'HH24:MI:SS FF6'));
end;
所以,我會先得到一個警報ID,然後我開始聽,然後在有一點我會發出警報(或不)。這是一個骨架,並且需要在實際設置中進一步細化。
非常感謝您的詳細回覆。我會仔細看看它,看看如何實現我的要求。我遇到的問題是我設法取回第一次提醒,但之後我不確定如何繼續進行輪詢以獲取更多提醒。無論如何,我會看看你的步驟。再次感謝。 – tonyf
@tonyf權利 - 我想這可能是問題。問題的確在於「保持輪詢」,因爲每次完成後您都必須開始新的呼叫。沒有什麼可以做的,這就是apex的工作原理 - 有時沒有一個頻道保持打開狀態,但仍有時會返回一些信息。這就是您沒有一段代碼等待警報的短暫失誤。這基本上是長時間投票。如果你不想要寬鬆的信息,你需要一箇中間系統,比如一個集合來存儲所有的信息並從那裏檢索。 – Tom