2017-03-16 118 views
1

使用Oracle APEX 4.2,我有一個頁面進程,它使用DBMS_ALERT作爲正在運行的進程,可能需要一分鐘才能完成或失敗。如何在Oracle APEX中使用Oracle DBMS_ALERT

在運行時,通過使用觸發器,有一個DBMS_ALERT.signal發佈進度通知。

我的問題是,在Oracle APEX 4.2中,如何在頁面上的區域或模態 中向用戶顯示來自DBMS_ALERT的這些通知的運行進度?

我需要在作業正在運行之前爲用戶提供所有這些警報,直到最終完成。

例如:

Started Job... 
Waiting on resources... 
Processing job... 
Job completed successfully. 

顯然通過每個這些DBMS_ALERTS的,每條線被呈現給用戶之前,可以存在幾秒鐘。

只是不確定如何在Oracle APEX中獲取這些警報並顯示用戶。

謝謝。

回答

0

我會在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,然後我開始聽,然後在有一點我會發出警報(或不)。這是一個骨架,並且需要在實際設置中進一步細化。

+0

非常感謝您的詳細回覆。我會仔細看看它,看看如何實現我的要求。我遇到的問題是我設法取回第一次提醒,但之後我不確定如何繼續進行輪詢以獲取更多提醒。無論如何,我會看看你的步驟。再次感謝。 – tonyf

+0

@tonyf權利 - 我想這可能是問題。問題的確在於「保持輪詢」,因爲每次完成後您都必須開始新的呼叫。沒有什麼可以做的,這就是apex的工作原理 - 有時沒有一個頻道保持打開狀態,但仍有時會返回一些信息。這就是您沒有一段代碼等待警報的短暫失誤。這基本上是長時間投票。如果你不想要寬鬆的信息,你需要一箇中間系統,比如一個集合來存儲所有的信息並從那裏檢索。 – Tom