如何在Oracle APEX中使用Oracle DBMS_ALERT?

时间:2017-03-16 04:41:06

标签: jquery sql ajax oracle11g oracle-apex

使用Oracle APEX 4.2,我有一个页面进程,它使用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中抓取这些警报并向用户显示。

1 个答案:

答案 0 :(得分:0)

我在apex.oracle.com上设置了一个演示,但由于您需要在dbms_alert上执行授权,因此它必须是纯文本的。

你可以在整个设置中走得很远,所以我认为这是基础的基础。 例如,我只使用了一个警报。在您的示例中,您可能希望使用多个事件来捕获不同的进度警报。这是因为简单的原因是,为了向客户端返回一些内容(ajax响应),ajax回调必须“关闭”。因此,当捕获警报并想要返回该警报时,您需要写入缓冲区并且需要返回它。这意味着你也将停止收听事件(阅读:在顶点,你应该!)。

考虑这样的流程:你将进行一个ajax调用,并有一个ajax回调过程,它记录了对事件的兴趣。然后等待警报发生。你捕获它并通过在http缓冲区(htp.p)中写入它来返回它。这是代码的结束,顶点将刷新缓冲区,然后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,然后我开始听,然后在某个时候我发送警报(或不发送警报)。它虽然是骨架,但在实际设置中需要进一步细化。