【问题标题】:How to use Oracle DBMS_ALERT within Oracle APEX?如何在 Oracle APEX 中使用 Oracle DBMS_ALERT?
【发布时间】:2017-08-07 02:49:17
【问题描述】:

使用 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 中获取这些警报并向用户显示。

【问题讨论】:

    标签: jquery sql ajax oracle11g oracle-apex


    【解决方案1】:

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

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

    考虑这样的流程:您将进行 ajax 调用,并有一个注册对事件感兴趣的 ajax 回调过程。然后等待警报发生。您可以通过将其写入 http 缓冲区 (htp.p) 来捕获并返回它。代码到此结束,apex 将刷新缓冲区,然后 ajax 调用将获取响应,您将能够管理该返回。
    不过不要忘记:apex 使用连接池,并且数据库会话不是直接链接的,而是一直重复使用的。您不想“离开”数据库会话“脏”。您也必须取消注册您的警报兴趣。这也为使用唯一 ID 的警报提供了一个案例 - 警报可以注册到不同的(数据库)会话中,所以如果这是一个可供多个用户用来跟踪其进程进度的页面,你不需要希望他们干扰其他用户的警报。

    但是,这种转瞬即逝的兴趣也意味着在不同的 ajax 调用之间会有“中断”。当您想要收听多个警报,并且这些警报可能非常紧密地打包在一起时,您可能会错过一个。假设 2 个警报间隔 1 毫秒:第一个将被捕获,报告给 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 对 - 我想这可能是问题所在。问题确实是“继续轮询”,因为每次通话完成后您都必须开始一个新的通话。您无能为力,这就是 apex 的工作方式 - 没有一个频道保持打开状态,但有时仍会返回一些信息。这就是你没有一段代码等待警报的地方可能会发生短暂的失误。这基本上是长轮询。如果您不想丢失信息,则需要一个中间系统(例如集合)来存储所有信息并从中检索。
    猜你喜欢
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多