2017-03-16 91 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