Oracle Apex中PL/SQL包全局变量意外重置问题排查
问题原因
PL/SQL包的全局变量是数据库会话级的,但APEX的Ajax请求依赖数据库连接池,每次请求可能分配到不同的数据库会话。你的foo函数通过apex.server.process触发时,若当前请求用的是新的数据库会话,包变量g_current_var和g_expire_date就会被重置为null,导致每次都要调用REST API。
解决方案(无需数据库表)
以下几种方案均基于APEX应用会话级存储,能在APEX会话内保留值至过期:
1. 使用APEX应用项(Application Item)
创建两个应用项:P_RANDOM_NUM(存储随机数)和P_EXPIRE_DATE(存储过期时间字符串),修改MAIN_PKG.foo函数逻辑:
FUNCTION foo RETURN VARCHAR2 IS l_random_num VARCHAR2(100); l_expire_date DATE; l_response CLOB; BEGIN -- 检查应用项中的值是否有效 IF :P_RANDOM_NUM IS NULL OR SYSDATE > TO_DATE(:P_EXPIRE_DATE, 'YYYY-MM-DD HH24:MI:SS') THEN -- 调用REST API获取新值 apex_web_service.g_request_headers(1).name := 'Content-Type'; apex_web_service.g_request_headers(1).value := 'application/json'; l_response := apex_web_service.make_rest_request( p_url => '你的API地址', p_http_method => 'POST', p_body => '{}' -- 按需调整请求体 ); -- 解析API响应(示例假设返回JSON格式) l_random_num := json_value(l_response, '$.random_num'); l_expire_date := TO_DATE(json_value(l_response, '$.expire_at'), 'YYYY-MM-DD HH24:MI:SS'); -- 更新应用项的会话状态 apex_util.set_session_state('P_RANDOM_NUM', l_random_num); apex_util.set_session_state('P_EXPIRE_DATE', TO_CHAR(l_expire_date, 'YYYY-MM-DD HH24:MI:SS')); END IF; RETURN :P_RANDOM_NUM; END foo;
应用项是绑定到APEX应用会话的,无论请求分配到哪个数据库会话,都能读取到同一批值。
2. 使用APEX缓存(APEX_CACHE)
APEX缓存支持键值对存储,并可设置过期时间,适合你的场景:
FUNCTION foo RETURN VARCHAR2 IS l_random_num VARCHAR2(100); l_expire_date DATE; l_response CLOB; BEGIN -- 从缓存读取值 l_random_num := apex_cache.get_string( p_cache_name => 'MAIN_PKG_CACHE', p_key => 'CURRENT_VAR' ); l_expire_date := apex_cache.get_date( p_cache_name => 'MAIN_PKG_CACHE', p_key => 'EXPIRE_DATE' ); -- 检查缓存是否有效 IF l_random_num IS NULL OR SYSDATE > l_expire_date THEN -- 调用REST API获取新值 apex_web_service.g_request_headers(1).name := 'Content-Type'; apex_web_service.g_request_headers(1).value := 'application/json'; l_response := apex_web_service.make_rest_request( p_url => '你的API地址', p_http_method => 'POST', p_body => '{}' ); -- 解析响应 l_random_num := json_value(l_response, '$.random_num'); l_expire_date := TO_DATE(json_value(l_response, '$.expire_at'), 'YYYY-MM-DD HH24:MI:SS'); -- 将值存入缓存,设置与API返回一致的过期时间 apex_cache.put_string( p_cache_name => 'MAIN_PKG_CACHE', p_key => 'CURRENT_VAR', p_value => l_random_num, p_expire_on => l_expire_date ); apex_cache.put_date( p_cache_name => 'MAIN_PKG_CACHE', p_key => 'EXPIRE_DATE', p_value => l_expire_date, p_expire_on => l_expire_date ); END IF; RETURN l_random_num; END foo;
APEX缓存默认是应用会话级的(也可设置为全局),能自动在过期时间后清理缓存值。
3. 前端会话存储(Session Storage)
如果随机数不涉及敏感数据,可在前端存储值,减少后端请求:
function fetchRandomNum() { const storedNum = sessionStorage.getItem('randomNum'); const storedExpire = sessionStorage.getItem('expireDate'); const now = new Date(); // 检查前端缓存是否有效 if (storedNum && storedExpire && new Date(storedExpire) > now) { return Promise.resolve(storedNum); } // 调用APEX Ajax流程 return apex.server.process('Get_Main_data', {}, { success: function(data) { // 将值存入sessionStorage sessionStorage.setItem('randomNum', data.randomNum); sessionStorage.setItem('expireDate', data.expireDate); return data.randomNum; } }); } // 打开模态框时调用 $('#modal').on('open', function() { fetchRandomNum().then(num => { // 使用随机数 console.log(num); }); });
注意:sessionStorage仅在当前浏览器标签页的会话中有效,关闭标签页后会清除。
内容的提问来源于stack exchange,提问作者another apex user
相关产品推荐
相关产品推荐

