简介:面向Oracle APEX初学者与具备一定Oracle基础的中级开发者,《Oracle APEX深入浅出》以单份doc技术文档形式,系统梳理从环境搭建到企业级应用开发的完整路径,重点解决APEX入门缺少体系、控件与报表使用不熟、页面美化和部署集成缺少参照等问题。压缩包内共1个doc文件,大小约8.11MB,内容按章节组织,涵盖APEX简介、账户与权限管理、开发概要、页面布局美化、常用控件、Report增删改、文件上传下载、CSS与JS应用、图表报表、插件使用、部署、调试技巧与API,以及APEX与EBS集成等模块。已有1593人学习,文档以开发指南形式串联知识点,便于读者按目录查阅环境配置、控件选型、报表设计、前后端脚本和集成调试思路,适合作为日常开发的手册与查漏补缺的参考。
1. APEX 到底解决什么问题:一个 PL/SQL 团队的非典型选择
接手一套跑在 Oracle EBS 旁边的内部系统,几十张表,需求方只提一条:浏览器里能查、能改、能导出。团队里全是写 PL/SQL 的老手,没人愿意为几个表单再上一套 Java 框架。Oracle APEX 就是在这种局面下被翻出来的——页面全部存在数据库里,渲染靠 PL/SQL 拼 HTML 往外吐,开发、部署、运行三件事都在浏览器里完成,客户端除了浏览器什么都不用装。这份 Oracle APEX 开发指南从卸载自带旧版、建表空间、配嵌入式网关讲起,一路覆盖账户体系、Grid Layout 页面布局、控件与 Dynamic Action、Tabular Form 多行增删改,最后落到 EBS 里挂 APEX 页面调用。适合手上有 Oracle 库、想把内部表单类应用快速搬到浏览器上的团队,也适合想搞清楚 APEX 到底能扛多大场面的人。
2. 环境搭建:从卸掉自带 3.0 到嵌入式网关跑通 4.2.5
这一章是整个资源里最经得起复现的部分,也是翻车最密集的部分。APEX 的安装不像装个中间件那样解压即用,它往数据库里塞了上千个 PL/SQL 对象和一个独立 schema,装错一次基本等于重来。下面按「先清场、再建库、后开口」的顺序走一遍,每一步都写清楚为什么。
2.1 为什么先卸载再安装:3.0 的残留会咬人
Oracle 11g 数据库自带 APEX 3.0,对应的 schema 是 APEX_030200 ;要装的 4.2.5 对应 APEX_040200 。两套 schema 名字不同,看起来互不干扰,问题出在访问入口上: /apex/apex_admin 这个 URL 最终走哪套包、登录校验调的是哪个版本的存储过程,取决于 EPG 配置里 DAD 指过去的位置和同义词解析顺序,而不是你以为的「新的覆盖旧的」。原文里那位作者改了三遍 ADMIN 密码、重启数据库都登不进去,根因就是老版本的校验逻辑还在生效。
动手前先确认库里到底躺着几套:
-- 以 SYSDBA 连接后执行,列出所有 APEX 相关 schema 及其状态
SELECT username,
account_status,
created
FROM dba_users
WHERE username LIKE 'APEX%'
OR username IN ('ANONYMOUS', 'APEX_PUBLIC_USER')
ORDER BY created;
这条查询的用处是把「隐形残留」变成「可见清单」。正常情况下 11g 新库只有 APEX_030200 、 APEX_PUBLIC_USER 、 ANONYMOUS 三个。如果看到多个 APEX_0x0x00 并存,就别急着装新版,先把老的清干净。 account_status 里如果是 LOCKED ,说明对应账户从没被解锁过,这在后面配网关时是要单独处理的,跟安装本身无关。
卸载脚本在旧版 APEX 的解压目录里,一般叫 apxremov.sql 。执行前把当前目录切到那份旧安装文件所在位置,用 SQL*Plus 以 SYSDBA 连库:
# 进到旧版 APEX 安装脚本目录,用 SYSDBA 执行卸载
cd $ORACLE_HOME/apex_old
sqlplus / as sysdba
-- 卸载旧版本,脚本会依次删除 schema 和同义词
@apxremov.sql
逻辑说明:这个脚本做三件事——删掉 APEX_030200 schema 及其全部对象、清理指向它的公共同义词、移除 EPG 里已注册的 DAD。参数说明:无入参,但执行过程中会提示确认,交互式输入 YES 。跑完之后用 2.1 那条查询复核一遍,确认 APEX_030200 已经不在了再往下走。注意别在同一个 SQL*Plus 会话里连续执行卸载和安装,中间断开重连一次,避免会话里缓存的同义词解析结果干扰判断。
2.2 表空间、安装脚本与中文语言包
APEX 自己的对象和静态文件建议单独放一个表空间,别跟业务表挤在一起。100M 起步、自动扩展、封顶 1G,对内部系统够用很久:
-- 给 APEX 单独划一块地,路径按实际库的 db_create_file_dest 改
CREATE TABLESPACE APEX01
DATAFILE '/u01/app/oracle/oradata/<SID>/apex01.dbf' SIZE 100M
AUTOEXTEND ON NEXT 100M MAXSIZE 1000M;
参数说明: DATAFILE 的路径别照抄,用 SHOW PARAMETER db_create_file_dest 看一眼实际存储位置再改; AUTOEXTEND ON NEXT 100M 是每次扩 100M,不是一次扩到 1G, MAXSIZE 才是上限。生产环境把这个上限调大一些,APEX 的元数据增长比想象中快,尤其是应用数量多、页面改动频繁的时候。
安装文件上传到 $ORACLE_HOME 下解压,然后执行安装脚本。这个脚本的参数顺序是固定的四个:APEX 对象表空间、APEX 静态文件表空间、临时表空间、图片虚拟路径。
# 解压到 ORACLE_HOME 下,避免后续权限和路径问题
cd $ORACLE_HOME
unzip apex_4.2.5.zip -d apex
cd apex
sqlplus / as sysdba
-- 四个参数:对象表空间、文件表空间、临时表空间、图片虚拟目录
@apexins.sql APEX01 APEX01 TEMP /i/
逻辑说明: apexins.sql 会先建 schema、再灌元数据表、最后把静态资源(CSS、JS、图片)写进数据库里的 XDB 仓库。第四个字面量 /i/ 是虚拟路径,浏览器请求 http://host:8888/i/... 时由网关映射到库内的资源,不是磁盘目录。参数说明:如果库的临时表空间不叫 TEMP ,用 SELECT property_value FROM database_properties WHERE property_name='DEFAULT_TEMP_TABLESPACE' 查一下再替换;图片路径保持 /i/ 不动,改了要同步改后面 EPG 配置里的映射。
装完顺手把中文语言包灌进去,不然开发者面对的是全英文界面,交付给业务方时也不好看:
# 语言包在解压目录的 builder/zh-cn 下,用 SYSDBA 执行
cd $ORACLE_HOME/apex/builder/zh-cn
sqlplus / as sysdba
-- 把中文语言包注册进 APEX 元数据
@load_zh-cn.sql
逻辑说明:语言包不是界面翻译文件,而是一批写进 APEX 元数据表的种子数据,装完要在 ADMIN 的「管理服务 → 消息」里把默认语言切成中文才生效。参数说明:无。这一步失败通常是因为当前会话的 schema 不对,脚本要在 APEX_040200 上下文里跑,报错就检查 ALTER SESSION SET CURRENT_SCHEMA 是否指对了。
2.3 嵌入式 PL/SQL 网关:8888 端口怎么开出来
11g 自带的 EPG 是最省事的入口,不用单独装 Apache 或监听器。配置脚本需要传 APEX 解压目录作为参数,把静态文件路径和 DAD 一次注册进去:
-- 参数是 APEX 解压目录,脚本会配好 /i/ 和 /apex/ 的映射
@apex_epg_config.sql /u01/app/oracle/apex
然后开端口。这一步是关键,不开端口的话服务起得来但外部访问不到:
-- 设置 EPG 监听端口,改完必须 COMMIT 才生效
EXEC DBMS_XDB.SETHTTPPORT(8888);
COMMIT;
-- 验证端口当前值
SELECT DBMS_XDB.GETHTTPPORT FROM DUAL;
参数说明:8888 是原文用的端口,换成 8080、80 都行,但 80 需要额外权限,内部系统建议避开。 COMMIT 那句不能省, SETHTTPPORT 是 DDL 语义的操作,不提交的话下一个会话看到的还是旧值——这个坑很多人踩过,改完不生效先怀疑没提交。
接下来处理两件账户的事。一是解锁匿名用户和公共用户,EPG 模式下未认证的请求走 ANONYMOUS ,认证流程走 APEX_PUBLIC_USER ,这两个锁着的话页面直接 404 或 401:
-- EPG 模式下这两个账户必须可用
ALTER USER APEX_PUBLIC_USER ACCOUNT UNLOCK;
ALTER USER ANONYMOUS ACCOUNT UNLOCK;
二是给 APEX_040200 开放出网权限。APEX 里如果用到了 Web 服务、远程图片、邮件这类功能,需要在 11g 的 ACL 里显式授权,否则运行时报 ORA-24247 :
-- 给 APEX schema 建一条 ACL,允许其访问外部主机
BEGIN
DBMS_NETWORK_ACL_ADMIN.CREATE_ACL(
acl => 'apex_net.xml',
description => 'APEX outbound network access',
principal => 'APEX_040200',
is_grant => TRUE,
privilege => 'connect');
DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL(
acl => 'apex_net.xml',
host => '*');
COMMIT;
END;
/
参数说明: principal 必须是 APEX 的 schema 名,写错成 APEX_PUBLIC_USER 不生效; host => '*' 表示不限目标主机,内部环境可以接受,收紧的话换成具体域名或 IP 段。这段脚本在 12c 之后语法有变化,跨版本迁移时不能直接搬。
最后改 ADMIN 密码。4.x 有独立脚本,别用 ALTER USER 硬改,因为 APEX 的 ADMIN 密码在元数据里还有一份校验信息,两处不一致会导致登录失败:
-- 交互式修改 ADMIN 密码,要求大小写+数字+特殊字符
@apxchpwd.sql
全部走完,浏览器打开 http://<host>:8888/apex/apex_admin ,能看到登录框就说明网关这一层通了。如果页面上只出现静态文字没有样式,说明 /i/ 的映射没配对,回头查 apex_epg_config.sql 的参数路径。
3. 账户、Workspace 与 Application:三级权限怎么落到人头上
APEX 的权限模型跟一般 Web 框架不一样,它不是「用户 → 角色 → 权限」那套,而是嵌套了四层:数据库 schema 在最底下,上面盖 Workspace,再上面是 Application,最上面才是用户。搞不清这四层的对应关系,后面配认证时全是玄学。
3.1 Workspace 与 Schema:一个工作空间就是一个工程
Workspace 是 APEX 里的隔离单位,一个 Workspace 绑定一个数据库 schema。同一个 schema 可以被多个 Workspace 绑,但一般不这么干,因为建 Application 时用的连接就是 Workspace 绑定的那个 schema,权限边界完全由它决定。
原文的做法是把 Workspace 直接绑到 EBS 的 APPS schema 上,理由是后续要跟 EBS 集成,省掉跨 schema 授权的麻烦。这个选择在开发环境能省事,生产环境要掂量:APEX 里写的每一条 SQL 都以 APPS 身份执行,等于把 EBS 的全部权限敞开了,页面里一个疏忽的 DELETE 就是事故。我一般的做法是开发期绑 APPS 图快,上线前单独建一个受限 schema,只授必要的对象权限和 synonym,代价是要补一批同义词,但换来的隔离值这个钱。
建完 Workspace 后想确认绑定关系,可以查元数据视图:
-- 以 APEX_040200 身份查,列出所有工作空间及其对应 schema
SELECT workspace,
workspace_display_name,
apex_users
FROM apex_workspaces
ORDER BY workspace;
参数说明: apex_users 是这个工作空间下的账户数,装上没多久就几十个的话,说明默认账户没清理干净,后面配权限时容易搞混谁是谁。视图名在 4.x 和 5.x 之间有变化,跨版本查的时候先 DESC 一下确认。
3.2 ADMIN、Workspace 管理员、开发者、终端用户四级
这四级的职责边界很清晰,混用的后果是权限泄漏或者开发效率低。
| 层级 | 能做什么 | 不能做什么 | 谁用 |
|---|---|---|---|
| ADMIN | 建/删 Workspace、管所有开发者账户、导实例 | 直接开发业务页面 | 只给一到两个人 |
| Workspace 管理员 | 管理本空间内的开发者、应用、导出 | 碰其他 Workspace | 项目负责人 |
| 开发者(Developer) | 建 Application、改页面、写 Process | 管账户、删 Workspace | 开发人员 |
| 终端用户(End User) | 登录跑应用 | 进 App Builder | 业务方 |
原文的操作路径是:先用 ADMIN 登进去建一个 Workspace(比如叫 ws_demo ,schema 选业务 schema),建的同时会要求指定这个 Workspace 的管理员;然后以这个管理员登录,在「管理用户」里加两类人——Developer 和 End User。这一步很多人图省事,把开发者直接加成 Workspace 管理员,结果是任何人都能改认证方案、能导出整个应用,出事之后查不到是谁动的。
判断某个账号在哪一层,用这条 SQL:
-- 查某个工作空间下的账户清单和角色
SELECT apex_user,
user_name,
is_admin,
is_application_user,
account_locked
FROM apex_workspace_apex_users
WHERE workspace = 'WS_DEMO';
is_admin 为 Y 是 Workspace 管理员, is_application_user 为 Y 是终端用户,两个都为 Y 说明既是管理员又能登录应用——这种账号应该尽量少,只留项目负责人一个。 account_locked 是独立开关,跟数据库账户的锁定不是一回事,APEX 里连续登录失败锁定的是这个字段。参数说明: workspace 要大写,小写查不出来。
3.3 Application 的登录认证与 Page 0 全局页
建 Application 时会让选认证方案,4.x 自带七种,常用的就两种。 Application Express Accounts 走 APEX 自己维护的账户表,适合纯内部、不跟外部系统打通的应用; HTTP Header Variable 依赖前置网关传头,适合已经有一套统一认证、需要免登的场景。选错了后期可以改,在「共享组件 → 认证」里换,但换完要把所有页面的授权方案跟着调,不然会出现「认证过了但页面进不去」的哑巴亏。
Page 0 是这个 Application 的全局页,不放任何可见组件,专门用来挂全局项(Global Page Items)和应用级计算。它不是必须的,但只要你的应用里出现了「每个页面都要用到的常量或当前登录人信息」,就该建。
常见的做法是在 Page 0 上建几个隐藏项,用 PL/SQL 计算出来:
-- Page 0 的计算项,类型选 PL/SQL Function Body
DECLARE
l_count NUMBER;
BEGIN
-- 取当前登录人在业务表里的岗位信息
SELECT COUNT(*)
INTO l_count
FROM t_user
WHERE login_name = :APP_USER;
IF l_count = 0 THEN
RETURN 'GUEST';
END IF;
RETURN 'STAFF';
END;
逻辑说明:结果赋给 G_ROLE 这样的应用级项,任意页面用 :G_ROLE 就能取到。参数说明: APP_USER 是 APEX 内置的会话变量,存当前登录名,不要自己再定义一个同名项。计算点的执行时机选「After Header」比较稳,选「Before Header」的话有些认证信息还没初始化完,取到空值。这段逻辑有个隐患——每渲染一页都查一次库,页面多、并发高的时候是笔开销,能缓存就缓存到应用项里,或者在登录后事件里算一次。
4. 布局、控件与 Tabular Form:把增删改从跳页改成不跳页
APEX 4.1 之前,区域列的宽度和位置是全局的,改一个区域会影响整页;4.2 之后引入了 Grid Layout,每个子区域都能单独配。理解这套配置的前提是知道它最终翻译成什么 HTML——不然你永远在「配了没效果」和「效果跟想的不一样」之间打转。
4.1 Grid Layout 的三个开关对应 TABLE/TR/TD
APEX 的主题页面结构本质上是 DIV + TABLE 混排,Body 区域渲染成 <TABLE> ,Position 区域渲染成 <DIV> 。Grid Layout 面板上那三个开关,一一对应 HTML 表格的层级:
| 开关 | 对应 HTML | 含义 |
|---|---|---|
| Start New Grid | <TABLE> | 当前区域是否另起一张新表格,列数可以重新定义 |
| Start New Row | <TR> | 当前区域是否换到新的一行 |
| Column | <TD> | 当前区域落在这一行的第几列 |
原文那个搜索区的例子配出来是这样:
| 区域 | Start New Grid | Start New Row | Column | 实际渲染 |
|---|---|---|---|---|
| Search1 | Yes | Yes | 1 | 新表格,第一行第一个格子 |
| Search2 | No | No | 2 | 同表格同行的第二个格子 |
| Search3 | No | Yes | 2 | 同表格新起一行,第二格,第一格空着 |
新手最容易犯的错是把三个开关当成独立的样式属性,实际上它们有依赖关系: Start New Grid 为 No 的前提是前面已经有一张表, Column 的值不能超过这张表已定义的列数。判断一个区域的配置对不对,最直接的办法是打开页面按 F12 看渲染出来的 DOM,对照表格结构一眼就能看出哪一层不对。
4.2 控件属性、校验与 Dynamic Action 的执行顺序
控件本身的属性面板不算复杂,真正需要记的是几个条件类属性: Value Required 、 Condition Type 、 Read Only Condition Type 、 Default value 、 Format Mask 。前三个是「什么情况下必填/显示/只读」,配的是 PL/SQL 表达式或 SQL 查询,不是布尔常量——想写死 TRUE 也能写,但那就失去了条件化的意义。
校验(Validation)和动态动作(Dynamic Action)是两套机制,执行时机完全不同。校验在服务端、提交时跑;Dynamic Action 在浏览器端、事件触发时跑。混用会出问题,比如指望 Dynamic Action 拦住非法数据提交——拦不住,用户禁用 JS 就绕过去了。
原文的例子是这样:点 SEARCH2_BTN 时校验 P1_X_TEXT 是否为空,但前提是 t_user 表里存在 user_id='123' 的记录。校验类型选 Function Returning Boolean ,条件类型选 EXISTS (SQL query returns at least one row) :
-- 校验表达式:返回布尔,true 表示通过
BEGIN
IF :P1_X_TEXT IS NOT NULL THEN
RETURN TRUE;
END IF;
RETURN FALSE;
END;
配套的条件 SQL:
-- 条件表达式,返回至少一行才执行上面的校验
SELECT 1
FROM t_user
WHERE user_id = '123';
逻辑说明:条件 SQL 返回零行时,整个校验被跳过,用户点按钮什么都不会发生——这是「条件不满足就静默通过」的设计,不是 bug。参数说明: :P1_X_TEXT 是页面项,冒号在 PL/SQL 里不能省; RETURN FALSE 触发的错误信息在 Error message 里配,显示位置建议选「Inline with Field」,比弹窗体验好。
Dynamic Action 这边,原文配了两个:一个是 P2_X_TEXT 值改变时,根据是否为空显示/隐藏 new search2 区域;另一个是页面加载时把当前日期塞进日期选择器。
-- Set Value 动作的 SQL,给日期控件赋初始值
SELECT SYSDATE FROM DUAL;
逻辑说明:第一个 DA 的事件选 Change 、选择类型选 Item(s) 、项选 P2_X_TEXT 、条件选 is not null ,True Action 是 Hide Region,False Action 是 Show Region。第二个 DA 的事件选 Page Load 、条件选 No Condition ,True Action 选 Set Value ,类型选 SQL Statement ,受影响元素选日期控件。参数说明: Change 事件不是每次按键都触发,是失焦后值确实变了才触发;要实时响应得用 Key Release 事件,但那样请求会很密集,表单类应用别这么干。
4.3 Tabular Form:基于视图的多行增删改与 Process
普通 Report 改一条记录要跳一个新页面,体验差、页面数还爆炸。Tabular Form 允许在一个表格里直接改多行、勾选删除、批量提交,代价是它比想象中难伺候。
原文的开发方式是基于视图建 Tabular Form,这跟 EBS Form 基于视图开发的思路一脉相承。建表和数据先准备好:
-- 班级表和用户表,用户表里带 class_id 外键
CREATE TABLE t_class (
class_id NUMBER,
class_name VARCHAR2(50)
);
CREATE TABLE t_user (
user_id VARCHAR2(50),
resp_id VARCHAR2(50),
resp_appl_id VARCHAR2(50),
login_name VARCHAR2(50),
class_id NUMBER
);
-- 供 Tabular Form 使用的视图
CREATE OR REPLACE VIEW v_user_class AS
SELECT u.user_id,
u.resp_id,
u.resp_appl_id,
u.login_name,
c.class_name
FROM t_user u,
t_class c
WHERE c.class_id = u.class_id;
这里有个必须提前说清楚的点:这条视图带 JOIN,在 APEX 里默认是 不可更新 的。建 Tabular Form 时它会让你指定主键, user_id 可以当主键用,但 class_name 这一列的写回需要额外处理——APEX 不会自动帮你反解到 t_class 表。原文的做法是绕开自动行处理,自己写 Process 全接管。
-- 提交按钮上的 Process,类型选 PL/SQL Anonymous Block
DECLARE
l_class_id NUMBER;
BEGIN
-- 遍历 Tabular Form 提交上来的行数组,G_F01 是勾选框
FOR i IN 1 .. NVL(APEX_APPLICATION.G_F01.COUNT, 0) LOOP
IF APEX_APPLICATION.G_F01(i) = '1' THEN
BEGIN
-- 按班级名反查 class_id,查不到就报错
SELECT class_id
INTO l_class_id
FROM t_class
WHERE class_name = APEX_APPLICATION.G_F03(i);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(
-20001,
'班级不存在:' || APEX_APPLICATION.G_F03(i));
END;
-- 同步更新 login_name 和 class_id
UPDATE t_user
SET login_name = APEX_APPLICATION.G_F02(i),
class_id = l_class_id
WHERE user_id = APEX_APPLICATION.G_F04(i);
END IF;
END LOOP;
END;
逻辑说明: G_F01 到 G_F04 是 Tabular Form 按列顺序分配的数组,具体编号取决于你建 Tabular Form 时勾选了哪些列、顺序如何,建完要到页面的「Tabular Form Attributes」里逐个核对,写错一位就是改错字段。参数说明: NVL(..., 0) 不能省,一行都没勾选时 COUNT 为 NULL , FOR 循环会直接抛异常; RAISE_APPLICATION_ERROR 的错误码必须在 -20000 到 -20999 之间,超出范围页面捕获不到。
还有一个执行顺序的坑:Tabular Form 自带「自动行处理」(Automated Row Processing),如果你同时用了自动处理和自己写的 Process,两者谁先跑取决于 Process 在页面上的排列顺序。想让自己的逻辑在自动处理之前跑(比如先校验再落库),就把它拖到自动处理上面;顺序反了,自动处理已经把脏数据写进去了,你再校验也没意义。
5. 避坑与排查:安装、登录、布局、流程的五条记录
下面这几条都是实际装、实际配的时候撞出来的,每条按「现象 → 原因 → 解决」写。装在测试库上试错成本低,生产库上撞一次够记很久。
5.1 装完登录不上,ADMIN 密码改三遍都没用
现象: /apex/apex_admin 页面能打开,输入 ADMIN 和新密码,转一圈回到登录页,没有任何错误提示。改密码、重启库、重装网关,全部无效。
原因:库里还残留着旧版本 APEX 的用户记录,登录校验走的是老 schema 下的过程。密码本身没问题,是校验入口指错了地方。原文作者最后用一条 SQL 查出来旧用户还在,drop 掉之后才通。
解决:装新版之前先按 2.1 那条查询确认 APEX_0x0x00 只剩一套。已经装完才发现的话,把残留 schema 连同其同义词一起清掉,再重新跑一遍 apxchpwd.sql 。清之前先导出 dba_users 和 dba_objects 里相关记录留档,别直接 DROP USER ... CASCADE 了事。
5.2 顺手 drop 掉 APEX_040200,只能重装
现象:清理残留用户时手滑,把新装的 APEX_040200 也 drop 了,或者 drop 完之后发现页面全部 500。
原因:这个 schema 不是普通用户,它装着 APEX 引擎的全部包和元数据,drop 掉等于把引擎拆了。APEX 没有「修复安装」这种选项。
解决:重新跑 apexins.sql 。重装前先把 EPG 里的 DAD 和 /i/ 映射清掉,否则新装的会跟残留配置打架。这个坑的教训很直接——清理账户的脚本一定要先 SELECT 再 DROP ,把用户名写死在脚本里,别用 LIKE 'APEX%' 这种模糊匹配去批量删。
5.3 Grid Layout 的 Column 设了没反应
现象:子区域的 Column 改成 3,保存后刷新页面,位置一点没变,还是挤在第二列。
原因: Start New Grid 为 Yes 的区域会新起一张表,新表的列数由第一个区域的 Column 值决定;后面的区域如果 Column 值超了这张表的总列数,会被浏览器忽略或者挤到下一行。另一种情况是父区域的 Start New Row 设成了 No ,子区域的列配置根本没机会生效。
解决:从最外层区域开始,逐层核对三个开关的依赖关系,别跳着改。改完按 F12 看 DOM,确认 <table> 的层数和 <td> 的个数跟预期一致。层级超过三层的话,建议先在纸上画一遍表格结构再配,比在界面上试错快得多。
5.4 Dynamic Action 选了 Change 但不触发
现象:文本框输入内容后,绑定的 Hide Region 动作没反应,手动刷新页面才看到效果。
原因: Change 事件的触发条件是「值改变且失焦」,用鼠标点进别的输入框才算失焦;用 Tab 键切换也算,但如果是在文本框里按回车提交,事件顺序会变成先提交再触发,看起来就像没生效。另一个常见原因是 DA 的选择类型选了 Item(s) ,但项名写成了 P2_X_TEXT 带上了页面前缀,实际应该只写项名或者反过来,取决于当前页面。
解决:先在浏览器控制台确认事件到底有没有触发(APEX 的调试模式下 DA 会打日志),再确认 When 条件里的项名跟页面项完全一致。临时把条件改成 No Condition 验证动作本身能不能跑,能跑就是条件问题,不能跑就是绑定问题。生产环境记得把调试关掉,调试模式下每次请求都写日志,量大了会拖慢响应。
5.5 Tabular Form 的 Process 跑完数据没变
现象:点提交按钮,页面提示成功,数据库里一条都没更新。
原因:三种可能。一是 G_F01 这种数组编号跟实际列顺序对不上, IF 判断永远为假;二是 Process 的执行时机配成了「Before Header」之类的前置点,那时行数组还没填充;三是视图带 JOIN 且没配主键保留,APEX 在提交前就把整行数据丢了。
解决:先在 Process 第一行加 APEX_DEBUG.MESSAGE('COUNT=' || APEX_APPLICATION.G_F01.COUNT) ,用调试模式看数组长度是不是零。是零就查执行时机和 Tabular Form 的主键配置;不是零就逐个打印 G_F0x 的值,对照页面上的实际输入。视图带 JOIN 的场景,老老实实关掉自动行处理,全部用自己的 Process 接管,别指望它自动反解。
6. 部署与验证:把应用从开发环境搬到验证环境的一套清单
APEX 的部署比想象中轻——应用本身就是一个导出文件,导入即用,没有编译打包那一步。但轻不代表没坑,导出的粒度选错、依赖对象没同步、认证方案跟着环境变了,这几种情况在验证环境里都会变成「本地好好的,一上去就报错」。
导出的粒度在 App Builder 里选,三个选项的区别值得记一下:导出整个应用(含所有页面、共享组件、授权方案)、只导页面、导应用但不含数据。跨环境迁移一般选第一个,导出的 .sql 文件里会带上应用 ID,导入到目标环境时如果 ID 冲突,APEX 会问你是覆盖还是新建,选新建会重新分配 ID,但页面里所有硬编码的应用 ID(比如 f?p=100:1 这种 URL)都会失效。
所以导入前先做一件事:把应用里所有硬编码的页面链接换成 APEX_PAGE.GET_URL 或 f?p=&APP_ID.:1 ,用绑定变量代替写死的数字。这一步在开发期做最省事,事后补改要翻遍每个页面。
验证清单按顺序走,别跳:
| 检查项 | 方法 | 失败表现 |
|---|---|---|
| 网关和端口 | 浏览器直接访问 /apex/apex_admin | 404 或连接拒绝 |
| 静态资源映射 | 页面有样式、按钮能点 | 纯文字无 CSS |
| 应用导入完整性 | 对比页面数、共享组件数 | 缺页面、缺 LOV |
| 认证方案 | 用终端用户账号登录一次 | 循环跳登录页 |
| 数据源连通 | 跑一遍主查询页 | ORA-00942 表不存在 |
| 提交链路 | 新增一条、改一条、删一条 | 提示成功但库里没变 |
跟 EBS 集成的那一步,原文放在最后讲,因为它是锦上添花而不是必需。做法是在 EBS 里注册一个 Function,类型选 JSP 或者 HTML ,把 APEX 页面的 URL 填进去,用户从 EBS 菜单点进去就是 APEX 页面;也可以在 Form 的按钮里直接调:
-- EBS Form 里打开 APEX 页面,URL 里带上业务参数
FND_UTILITIES.OPEN_URL(
'http://<apex-host>:8888/apex/f?p=100:1:' ||
:PARAMETER.SESSION_ID || '::NO::P1_USER_ID:' || :USER_ID);
参数说明: p=100:1 里的 100 是应用 ID、1 是页面 ID,换应用就得改; SESSION_ID 拼进去是为了让 APEX 复用 EBS 的会话上下文,不拼也能用,但用户每次点进去都要重新登录。这段代码在 11i 和 R12 上都能跑,但 URL 长度有限制,参数多的话建议只传关键 ID,其余在 APEX 端查。
这套东西我从装第一遍到现在,每次上新环境都要按第 5 章那几条重新核一遍,尤其是「卸旧版」和「drop 账户」这两步,宁可多花十分钟 SELECT 确认,也不愿意再体验一次重装。希望帮到你。
本文系统讲解Oracle APEX 4.2.5环境搭建、三级权限体系(ADMIN/Workspace/Developer/End User)、Grid Layout页面布局、Dynamic Action执行机制、Tabular Form多行增删改实现,以及与Oracle EBS集成的关键配置。重点涵盖嵌入式PL/SQL网关配置、中文语言包部署、ACL网络权限设置、认证方案选型与Page 0全局页应用,并总结安装、登录、布局、流程五大典型避坑方案。
4673

被折叠的 条评论
为什么被折叠?



