我会在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