简介:《Oracle APEX深入浅出》是一份面向Oracle APEX初学者与中级开发者的技术文档,侧重解决从环境搭建到企业级应用开发中的知识断层问题。资源为单个doc文档,压缩包约8.11MB,内容以开发指南形式组织,适合作为系统学习与日常查阅的参考材料。文档目录覆盖APEX系统探究、账户与权限管理、页面布局美化、常用控件、Report增删改、文件上传下载、CSS与JS应用、图表报表、插件、部署及调试技巧,并延伸至APEX与EBS集成等场景。读者可据此梳理APEX开发主线,理解控件与报表的配合方式,掌握文件处理、前端脚本和数据可视化的实现思路,也能从调试、常用API与部署章节中获得排错和上线参考。已有1593人学习,适合需要夯实基础并串联实战环节的APEX开发人员。
1. APEX 能干什么:一个把 PL/SQL 直接变成 Web 页面的数据库内框架
接手一套跑在 Oracle 上的 ERP,业务方丢过来一句“这个查询和修改页面下周要上线”,而你手里只有 SQL*Plus、一个浏览器和一堆 PL/SQL 包——这种场景下 Oracle APEX 是性价比最高的一条路。它把整站应用都存进数据库:页面定义是表里的行,渲染逻辑是 PL/SQL 包,浏览器发来的 URL 被解析成一次 PL/SQL 调用,结果用HTP.P拼成 HTML 吐回去,客户端除了浏览器什么都不用装。这份围绕 APEX 4.2.5 整理的开发笔记,从卸载数据库自带的老版本、建表空间、跑安装脚本讲起,一路到 Workspace 权限、Grid Layout 排版、Tabular Form 多行增删改和 Dynamic Action 的无刷新交互。它适合两类人:天天写 PL/SQL、想把数据快速变成界面的数据库开发;以及做 ERP 套件二次开发、需要把 Form 逻辑搬上浏览器的工程师。纯前端同学会觉得声明式配置有点笨,但配完之后确实省掉大量样板代码。
2. 环境搭建:从 11g 自带 3.0 升到 4.2.5 的完整路径
很多人在这一步卡三天,不是因为命令难,而是因为数据库装完就自带一个 APEX 3.0,你以为装的是新的,实际登录走的是旧的。先把版本底线说清楚:这份笔记里的 Grid Layout 子区域重排、Tabular Form 上的 Dynamic Action,都是 4.2 之后才顺手的,3.0 上要么没有要么难用,所以升级不是可选项。环境是一台虚拟机上的 RedHat + Oracle 11g,安装包用 apex_4.2.5.zip,网关走数据库自带的嵌入式 PL/SQL 网关,不额外装 Apache 和 mod_plsql。下面按实际操作顺序拆。
2.1 先卸干净 3.0,再谈安装新版本
自带的 3.0 不会因为你装 4.2.5 就自动消失。表现是:管理员页面能打开,输入 ADMIN 和密码之后提示授权失败,你把 ADMIN 密码改了三遍、数据库重启两次,问题依旧。原因是认证链路上还留着 3.0 的 schema 和它的同义词,4.2 的登录流程被老对象截胡了。动手前先做一次体检。
-- 以 sysdba 身份连库,列出所有 APEX 相关 schema select username, account_status from dba_users where username like 'APEX%' or username in ('ANONYMOUS', 'FLOWS_FILES') order by username;逻辑说明:APEX 的实例账号命名规则是APEX_加版本号,4.2 对应APEX_040200,3.0 对应APEX_030200。这条 SQL 只要查出两个版本号并存,就基本能定性了。参数说明:username取自数据字典dba_users,account_status用来确认账号是不是被锁(LOCKED)或过期(EXPIRED),排查登录问题时这两列一起看。
确认存在 3.0 残留之后,用安装目录里的卸载脚本清理,而不是手工 drop。
# 进入解压后的 APEX 目录,执行官方卸载脚本 cd $ORACLE_HOME/apex sqlplus / as sysdba @apxremov.sql注意:这个脚本会把整个 APEX schema 连同里面的应用数据一起删掉。生产库上动手之前先expdp导一份,卸载脚本不提供回滚。这也是我第一次操作时留下遗憾的地方。
2.2 表空间、解压和静默安装
APEX 自身的元数据不大,一个大头是应用里的文件和会话状态,所以表空间不用给太夸张,但要开自动扩展防止跑一半写满。
-- 单独给 APEX 建一个表空间,避免和其他业务数据抢空间 CREATE TABLESPACE apex01 DATAFILE '/d01/oracle/ORCL/db/apps_st/data/apex01.dbf' SIZE 100M AUTOEXTEND ON NEXT 100M MAXSIZE 1000M;参数说明:DATAFILE的路径建议跟库里其他数据文件落在同一个挂载点,省得后面监控脚本漏掉这一块盘;SIZE 100M是起步值,够装几百个页面;AUTOEXTEND ON NEXT 100M表示每次不够就扩 100M;MAXSIZE 1000M是封顶,防止某个应用疯狂写临时文件把盘吃满。中小项目 1G 足够,真到不够的时候你该考虑的是归档和清理策略,而不是继续加 MAXSIZE。
接着把安装包传到$ORACLE_HOME下解压。目录名必须是apex,安装脚本里用的是相对路径,你改成apex425之类它找不到文件。
cd $ORACLE_HOME unzip apex_4.2.5.zip # 解压完应该能看到 apxins.sql、apxconf.sql、apxremov.sql 这几个关键脚本 ls apex/*.sql | head安装本身是一条命令,交互式问答走完就完事。
cd $ORACLE_HOME/apex sqlplus / as sysdba @apxins.sql过程中会问你三个关键信息:表空间名(填刚才建的APEX01)、APEX_PUBLIC_USER的密码、ADMIN的密码。口令策略是硬校验,不是建议——ADMIN 的密码必须同时包含大写字母、小写字母、数字和特殊字符,不满足直接抛错退出,白等一轮。这个坑在后面第 5 章还会以另一种形式出现。
2.3 装完之后的三个开关
安装脚本跑完不等于能用,还有三件事要做:解锁匿名用户、给 APEX schema 开网络访问权限、按需装中文语言包。
-- 1) 解锁匿名用户,否则浏览器匿名访问会被拒 ALTER USER ANONYMOUS ACCOUNT UNLOCK; -- 2) 给 APEX 的 schema 放开任意主机的连接权限(嵌入式网关下必须) BEGIN DBMS_NETWORK_ACL_ADMIN.create_acl( acl => 'apex_net.xml', description => 'APEX network access', principal => 'APEX_040200', is_grant => TRUE, privilege => 'connect' ); DBMS_NETWORK_ACL_ADMIN.add_privilege( acl => 'apex_net.xml', principal => 'APEX_040200', is_grant => TRUE, privilege => 'resolve' ); END; /逻辑说明:第一步解锁ANONYMOUS,是因为浏览器在没登录之前是以匿名身份访问登录页的,账号锁着就看不到登录框。第二步是 11g 之后的 ACL 机制,APEX_040200作为 schema 需要显式授权才能向外发起连接,缺了这一步,页面里做 Web 服务调用会报ORA-24247。参数说明:principal写的是数据库用户名,大小写按字典里的实际值;privilege的connect管发起连接,resolve管域名解析,两个通常一起给。
中文语言包是可选项,在apex/builder/zh-cn目录下用sqlplus跑一遍就行,装完在管理界面里切换语言。多人协作的项目建议统一装上,不然导出应用时语言相关的文本会来回变。
2.4 嵌入式 PL/SQL 网关与 8888 端口自检
不用额外装 HTTP 服务器,11g 自带 EPG,省掉了 Apache 加 mod_plsql 那一整套。配置靠一条脚本,它会建 DAD、设端口、把APEX_PUBLIC_USER挂上去。
# 配置嵌入式网关,交互里确认端口,默认 8888 cd $ORACLE_HOME/apex sqlplus / as sysdba @apxconf.sql跑完先别急着开浏览器,按顺序验三件事。第一条,监听有没有把端口开出来:lsnrctl status输出里应该能看到 8888 在监听,没有就是 EPG 没起来。第二条,DAD 存不存在:select dad_name, path from dba_epg_dad_attributes group by dad_name, path;,正常应该有一条apex对应/apex/*。第三条,直接敲http://<数据库主机>:8888/apex/apex_admin,能出登录框就通了。
如果这里报 HTTP 404,八成是 EPG 的端口没起或者 DAD 没建上。文档里的做法是执行对应命令把它启起来,我的习惯是先查dba_epg_dad_attributes确认 DAD 在不在,再决定是重建 DAD 还是重启监听,避免一上来就重启数据库——重启能好,但你不知道它为什么坏,下次还会坏。
3. 账户体系与页面开发:Grid Layout 是 APEX 的排版地基
APEX 的开发体验是声明式的:拖控件、填属性、配条件,不用编译,代码量主要落在 PL/SQL 上。但“声明式”不等于“不用理解底层”,尤其是页面布局和交互,配置项背后全是 HTML 元素和 DOM 事件。这一章把账户模型、Page 0、Grid Layout 和 Dynamic Action 串起来讲,都是实际做事时会反复打开的面板。
3.1 ADMIN、Workspace、Developer 三层账户模型
APEX 的权限是三层结构,搞不清这三层,会一直在登录页打转。
第一层是ADMIN,实例级超级管理员,住在数据库里,跟任何应用无关。它能做的事是创建、删除、修改 Workspace 和 Workspace 管理员。第二层是 Workspace,本质上是一个工作空间,一个 Workspace 绑定一个数据库 Schema——你可以把它理解成一个工程,工程里有很多子项目。第三层是 Workspace 里的三种角色:管理员(Admin)、开发者(Developer)、应用最终用户(App End User)。
创建顺序是有讲究的:先用 ADMIN 建 Workspace(同时指定它绑哪个 Schema,比如直接绑到 ERP 套件的APPS,这样 APEX 页面和 ERP 的视图、包在同一片数据上),再给这个 Workspace 建管理员,然后用管理员登录建开发者和最终用户。这个顺序反了会出现“为什么我的开发者登录看不到应用”这类问题——因为开发者必须在 Workspace 内部创建,跨 Workspace 看不见对方的应用。
每个应用还要单独配认证方式。创建应用时会让你三选一,之后在 Shared Components 里可以改,APEX 自带 7 种认证方案。实际项目里常用的是两种:Application Express Accounts,走 APEX 自己的账号密码;HTTP Header Variable,信任前置系统传过来的头信息。做 ERP 集成时选后者,用户从 ERP 菜单点进来时已经登录过,不该再弹一次 APEX 登录框。
3.2 Page 0 与页面模板的 Body / Position 区域
Page 0 是这个应用的一个全局页,不属于任何具体页面。全局变量、全局常量、公共的 PL/SQL 声明都放在这里,相当于整站初始化块。做法是在 Page 0 建几个 HTML Region,把渲染条件设成永远不满足,它就只求值不显示,效果类似 HTML 的 hidden 字段,但因为是服务端求值的,比 hidden 能装的东西多得多。为了后面好维护,我习惯按类型分区:日期常量一个区、业务开关一个区、权限判断一个区,别全堆一块。
主题决定了页面的骨架。APEX 自带 20 套主题,每套由定义外观的模板组成,4.0 之后的主题都符合 XHTML 和 CSS 标准,页面体积更小、渲染更快。主题会把页面切成若干个命名位置:Body 1、Body 2、Body 3、Position 1到Position 4。这些位置在最终 HTML 里是不一样的:Body 区渲染成<TABLE>,Position 区渲染成<DIV>。你选的 Parent Region 是哪个位置,直接决定了你的区域是被塞进表格单元格里,还是独立成一个 div。
记住这条对应关系,后面布局配错了你能立刻反应过来说“哦,我 Parent 选错了”。
3.3 Grid Layout 的三个开关:Start New Grid、Start New Row、Column
页面布局这块最有价值的一条经验:只要你能把目标版面画成一张表格,Grid Layout 的配置就是翻译工作,不是玄学。
Start New Grid:是否用一个新的<TABLE>开始,选 Yes 意味着这个区域不继承上一个区域的表格结构,可以重新定义列数。Start New Row:是否换行,对应<TR>。Column:落在当前行的第几列,对应第几个<TD>。
拿文档里的实例对照。Search1、Search2、Search3三个区域属于同一个 TABLE,配置是:Search1 的Start New Grid=Yes、Start New Row=Yes、Column=1;Search2 的Start New Grid=No、Start New Row=No、Column=2;Search3 的Start New Grid=No、Start New Row=Yes、Column=2。翻译过来就是:开一张新表,第一行第一格放 Search1,第一行第二格放 Search2,然后换行,第二行第二格放 Search3。第二行第一格空着,视觉上就是 Search3 向右缩进了一格。
嵌套的New Search1到New Search3是同样的逻辑,只是 Column 依次填 1、2、3,全在同一行。更复杂的布局也是这个原则往上叠,配着配着就出来了。
这里有个 4.1.1 时代的坑值得单独说:Column属性在同一个 Body 内是全局生效的,你改一个区域的列宽或列号,会连带影响这个 Body 里其他区域。当时做项目被这个折腾得很郁闷,只能靠整体重排绕开。4.2.5 引入了子区域的独立 Grid Layout,每个子区域有自己的配置,改一个不影响别人,这才算真正好用。
3.4 控件属性、校验和 Dynamic Action
APEX 的控件种类覆盖了常见需求:按钮、文本框、下拉框(Commbox)、值列表(LOV)、日期选择器、报表。控件本身不难,难的是属性组合,按用途分四组看会清晰很多。
呈现类:Form Element Width控宽度,Horizontal / Vertical Alignment控位置和 Label 的摆放。控制类:Value Required必填开关,Condition Type决定显示条件(不满足就不渲染),Read Only Condition Type决定只读条件。数据类:Default value默认值,Format Mask格式掩码——日期和数字一定要配Format Mask,否则显示格式跟着数据库的NLS_DATE_FORMAT走,换台机器就是另一副样子。事件类:Condition Type是执行前提,True Actions/False Actions是条件真假时分别干什么,WHEN Condition是动作自己的判断条件。
校验也有一组属性:When Button Pressed指定触发按钮,Condition Type加Condition Expression控制校验前提,Validation是校验逻辑本身,Error message display location控错误信息插在哪,Associated Item把错误挂到具体控件旁边。
先看一个校验的写法,需求是:点 Search2 按钮时,如果P1_X_TEXT为空就报错,但只有jjl_test表里存在user_id='123'这条记录时才校验。
-- Page Item: P1_X_TEXT 的 Validation -- 触发按钮:SEARCH2_BTN -- Condition Type: Exists (SQL query returns at least one row) -- Condition Expression: select 1 from jjl_test jt where jt.user_id = '123'; -- Validation Type: Function Returning Boolean begin if :P1_X_TEXT is not null then return true; -- true 表示校验通过,继续往下走 end if; return false; -- false 表示拦截,并显示 Error message end;逻辑说明::P1_X_TEXT是页面项绑定变量,冒号不能省,省了就是编译报错。返回 boolean,APEX 引擎拿到false就中断提交并把Error message显示出来。参数说明:Condition Expression那段 SQL 的作用是“有条件地校验”——某些数据状态下不需要校验,硬校验反而挡住正常流程;Associated Item填P1_X_TEXT,错误提示就会显示在这个输入框旁边,而不是页面顶部一行看不懂的英文。
再看 Dynamic Action,这是 APEX 里最像 Ajax 的东西,能干的事包括执行 PL/SQL、设值、显隐区域,全程不刷新页面。两个典型用法:
第一个,页面加载完给日期控件赋当前日期。事件选Page Load,动作选Set Value,Set Type选SQL Statement,SQL 写select sysdate from dual;,然后在Affected Elements里把Selection Type设成Item(s),Item(s)填日期控件的页面项名(示例里叫DATA)。
-- Dynamic Action: page_load_set_date -- 事件: Page Load / 条件: No Condition -- 动作: Set Value → Set Type: SQL Statement select sysdate from dual;这段 SQL 只有一列,APEX 取第一行第一列的值赋给目标项。参数说明见上面:真正决定“赋给谁”的是Affected Elements里那个 Item 名,不是 SQL 本身。这块是最容易翻车的地方,只写了 SQL 没选目标项,页面跑起来一点反应没有,你会怀疑是缓存问题,其实配置根本没生效。
第二个,P2_X_TEXT的值变化时,如果非空就隐藏new search2区域,为空就显示。事件Change,Selection Type选Item(s),When Item(s)填P2_X_TEXT,When Condition选is not null,True Actions挂Hide Region,False Actions挂Show Region。这样用户一边输入一边就看见区域在动,不需要点任何按钮。
4. Tabular Form 增删改:基于视图的多行编辑与自定义 Process
APEX 里改一条记录的主流做法是链接到新页面,但批量场景下这个体验很差——改十行要跳十次。Tabular Form 允许在一个表格里直接改多行、勾选多行,然后一次提交。它的开发方式和 ERP 套件的 Form 很像:数据源基于视图,逻辑写在 Process 里。下面用一组表把这个流程走完。
4.1 用视图做数据源,而不是直接绑表
为什么不直接绑表?因为你要在页面上显示可读的分类名,而不是一堆 id;同时 Tabular Form 需要每行有稳定的行标识才能把提交上来的数据对回去。视图把 join 之后的结果一次喂给页面,配置简单,代价是视图默认不可更新——所以写回逻辑必须自己实现。这套“视图读、Process 写”的分工,是这类页面最稳的结构。
-- 分类字典表 create table jjl_class ( class_id number, class_name varchar2(50) ); -- 页面数据源视图:把用户表和分类字典拼在一起 create or replace view jjl_class_test2 as select jt.user_id, jt.resp_id, jt.resp_appl_id, jt.login_name, jc.class_name from jjl_test2 jt, jjl_class jc where jc.class_id = jt.class_id;逻辑说明:jjl_test2存业务数据,jjl_class是分类字典,通过class_id关联。视图里user_id作为每行的稳定标识,class_name是从字典表翻译过来的可读名——用户在页面上直接改分类名,Process 里再用名字反查 id 写回去。参数说明:class_id是关联键,必须有索引,不然 Tabular Form 一页刷几十行就开始慢;user_id是主键性质的字段,页面上要设成只读,别让用户改。
建好视图之后,在 APEX 里新建页面、选 Tabular Form 类型,数据源指向jjl_class_test2,列顺序就是视图里 select 的顺序——这个顺序后面会直接决定g_f数组的下标,千万别随手调整。
4.2 Apply Change 按钮与 Process 的绑定
页面建好后加一个 Region Button,名字叫Apply Change,Action 选Submit Page。注意不是Redirect,选错了页面会跳走,一行数据都提交不上。
然后建 Process,两个配置项必须对:Point选On Submit - After Computations and Validations,When Button Pressed选APPLY_CHANGE。执行顺序是:校验先跑、Process 后跑、最后才提交事务。如果 Process 放到Before Computations,页面项的旧值会盖掉你的更新,表现就是“改了没生效但也没报错”,这种 bug 最难查,因为日志里什么都没有。
这一步做完先别写业务逻辑,放一个空的 Process 进去,点一下按钮,看页面有没有正常提交、有没有报错。基线通了再往里加东西,出问题能立刻定位是配置还是代码。
4.3 在 Process 里做业务校验和二次写入
需求是:改class_name和login_name时,先校验class_name在字典表里存不存在,不存在就报错并拦住整批提交;存在就把当前行的login_name规范化后写回,同时更新分类 id。
declare l_class_id jjl_class.class_id%type; begin -- g_f05 对应 Tabular Form 里第 5 列(class_name) -- 未勾选的行不会出现在数组里,勾选列决定了哪一行被提交 for i in 1 .. nvl(apex_application.g_f05.count, 0) loop -- 1) 按名字反查字典,查不到直接拦住 begin select class_id into l_class_id from jjl_class where class_name = apex_application.g_f05(i); exception when no_data_found then raise_application_error(-20001, '分类不存在:' || apex_application.g_f05(i)); end; -- 2) 校验通过,把这一行的登录名统一转大写后落库 update jjl_test2 set login_name = upper(apex_application.g_f04(i)), class_id = l_class_id where user_id = apex_application.g_f01(i); end loop; end;逻辑说明:apex_application.g_fNN是 APEX 对 Tabular Form 各列的数组绑定,NN是列在报表定义里的序号,从 1 开始,所以数组下标和视图 select 的列顺序是强绑定的。循环体先做存在性校验再更新,raise_application_error抛出的错误会被 APEX 捕获并渲染到页面上,同时整批回滚——这样不会出现“前十行改成功、第十一行报错”的脏数据。参数说明:.count是本次提交上来的行数;索引i从 1 到count;nvl(...,0)是防御性写法,一行没勾的时候循环体不执行,避免ORA-06502。
有个细节文档里没细说但很关键:g_f数组的下标和报表列序号对不上,是这类页面最高频的故障。稳妥的做法是在 Process 里临时加一句apex_debug.message('f01='||apex_application.g_f01(i));,把数组打出来对一遍,确认无误再删掉。靠猜列号能让一下午蒸发。
跑通之后的效果是这样:输入一个字典里没有的分类名,点 Apply Change,页面报“分类不存在”;改成存在的分类名,提交成功,数据落库。批量改十行也只点一次按钮。
4.4 文件上传、图表和插件的挂载点
Tabular Form 之外的几个常用块,位置得先搞清楚,不然会到处找菜单。
文件上传有两条路线。小文件走 BLOB 直接入库:控件选 File Browse,提交后在 Process 里从APEX_APPLICATION_TEMP_FILES取blob_content写进目标表的 BLOB 列,注意这个临时表在会话结束时清空,必须当次处理掉。大文件走文件系统:在数据库里建DIRECTORY,只存路径和文件名,读写用BFILENAME。下载通常是建一个隐藏页面项加上APEX_UTIL的输出流程,或者挂一个专门的下载页,用f?p=带参数触发,别把文件流塞进主页面。
图表和报表都在 Region 类型里选。图表的数据源写一段 SQL,聚合列的顺序决定图例和数据点,Group By的粒度决定横轴颗粒度;表格类报表用 Interactive Report 适合让用户自己筛,Classic Report 适合固定格式的输出。文档里还提到共享的免费插件库,装完在Shared Components → Plug-ins里能看到。这里有个部署坑:插件是跟着应用走的,导出应用时一定要勾上插件,否则换实例部署会报plug-in not found,页面直接白屏。
5. 避坑与排查:五条翻车记录和它们的定位路径
APEX 的报错信息普遍比较含蓄,登录失败不告诉你是哪一层失败,页面没反应也不告诉你是配置还是代码。所以排查的第一步永远是缩小范围:是数据库层、网关层、应用层还是页面层?下面五条都是我实际撞过的,按“现象 → 原因 → 解决”记下来,遇到同类问题可以直接对号。
5.1 安装与登录阶段:残留账号和误删的后悔药
坑一:装完 4.2.5,ADMIN 登录一直提示授权失败。
现象:管理员页面http://host:8888/apex/apex_admin能正常打开,输入 ADMIN 和密码后回到登录页或提示认证不通过;改密码、重启数据库都无效。
原因:数据库里还留着一批 3.0 版本的 schema 和同义词(APEX_030200及相关对象),APEX 的认证流程先命中了老版本的对象。系统能起来说明安装本身没问题,问题在认证链路上选错了对象。
解决:按 2.1 节那条 SQL 查一遍dba_users,确认两个版本号并存后,用apxremov.sql卸干净再重装。文档里还提到一个更彻底的做法:把残留的老用户逐个drop user ... cascade掉,然后再装。我一般会先卸再装,比手工 drop 稳妥,因为手工 drop 容易漏掉同义词和 ACL。
坑二:清理残留的时候,手滑把APEX_040200也 drop 了。
现象:登录彻底不可用,报ORA-04043: object APEX_040200 does not exist之类的错误,所有页面 500。
原因:APEX_040200是 4.2 的核心 schema,里面装着全部的页面元数据和 PL/SQL 包,drop 之后没有软删除这一说。
解决:只能重装。重装过程本身不复杂,但如果里面有已经开发好的应用,那就全没了。从那以后我养成一个习惯:任何drop user之前先select username from dba_users where username like 'APEX%'把要删的东西列一遍,确认只有目标行再执行。后悔药这东西,数据库一般不卖。
坑三:虚拟机挂起恢复之后,8888 端口打不开。
现象:昨天还好好的,今天开虚拟机,浏览器访问http://host:8888/apex/返回 HTTP 404 或者干脆连不上。
原因:挂起再恢复,EPG 相关的服务和监听不一定跟着回来,DAD 也没挂上。文档里给的建议是把虚拟机状态设成挂起而不是关机,既有恢复速度,也能少遇到这类问题。
解决:先lsnrctl status看 8888 在不在监听,不在就重启监听;在监听还 404 的话,查dba_epg_dad_attributes确认 DAD 存在,必要时重跑apxconf.sql。排查顺序永远是先网络后配置,别一上来就重建应用。
5.2 运行与页面阶段:布局和动态动作
坑四:Grid Layout 配完,区域挤在一起或者整体跑偏。
现象:明明每个区域都填了 Column=1/2/3,页面上却变成一列往下堆,或者几个区域叠在同一个格子里。
原因:Start New Row忘了打。Column只决定“落在第几格”,不决定“换不换行”,不换行的话所有区域都往当前行的下一格挤,超过列数就自动折下去,看起来就像全堆在一起。另一个原因是Start New Grid该选 Yes 的地方选了 No,导致继承了上一个区域的表格列数。
解决:把目标版面先画成表格,逐个区域核对三个开关。4.1.1 上还要额外注意Column在同一 Body 内是全局的,改一个会影响别人;4.2.5 之后的子区域独立 Grid Layout 没有这个问题,这也是我建议直接上 4.2 的原因之一。
坑五:Dynamic Action 配好了,页面上毫无反应。
现象:条件、事件、动作都填了,点按钮或者改输入值,区域不动、值不变,浏览器也不报错。
原因:九成出在Affected Elements。Set Value、Hide Region、Show Region这些动作都必须指定作用对象,只写了动作没选目标项,动作执行了但什么都没影响到。另外Selection Type选错也会这样——目标是区域却选了Item(s),APEX 找不到对应元素,静默跳过。
解决:打开浏览器开发者工具的网络面板,操作触发后看有没有发出 ajax 请求。没有请求说明事件没绑上,重新核对When Event和When Selection Type;有请求但返回里看不出变化,就是Affected Elements的问题,回去把Selection Type和具体的 Item/Region 名填对。这条经验救过我不止一次:先看有没有请求,能立刻把问题劈成两半。
6. 二次开发:把 APEX 应用当代码管
APEX 最容易被误会的一点,是它看起来“没有代码”,所以很多团队把它当成纯配置工具,改动全靠记忆和口头交接。真到了要回滚、要对比、要往另一个实例部署的时候,问题全出来了。所以我在做这类项目时,第一件事不是画页面,而是把“应用即代码”这条规矩立起来。
核心动作是导出。在 Application Builder 里选 Export,把 Application 和 Supporting Objects 一起导成 SQL 脚本,放进版本库。这个脚本本质是一批wwv_flow_api调用,逐条重建页面、区域、控件、Process,虽然没法像 Java 那样逐行 review,但至少能diff。改完之后再导一次,用版本控制工具对比,改动落在哪个页面、哪个区域、哪一行配置,一目了然。这一招同时也是回滚方案:出问题就用上一版脚本重导一次。
第二个习惯是把页面里的匿名 PL/SQL 尽量挪进数据库包。页面里的 Process 写几十行匿名块看着很方便,但它没法单独跑测试、没法被别的页面复用,导出的 SQL 里也全是最难读的噪音。我的做法是:Process 里只留一行pkg_xxx.process_apply_change(...),真正的逻辑进包,参数显式传页面项。这样逻辑能单独验证,包还能进常规的数据库发布流程。
第三个是命名规范,这条在 APEX 里比在别的技术栈里更重要。页面项统一P<页号>_<业务缩写>_<字段>,比如P2_ORD_STATUS;按钮统一BTN_<动作>;Process 统一P_<页号>_<动作>。为什么重要?因为 Tabular Form 的g_fNN数组靠列序号取值,页面项名混乱的时候,你没法靠名字反推校验,只能一遍遍去报表定义里数列。命名清晰,第 4 章那种按列取值的逻辑才敢放心写。
最后是部署验证。导出脚本、导到测试实例、跑一遍关键路径,这三步固定走完再上生产。导的时候记得勾插件,前面说过坑。做 ERP 集成的话,菜单里配一个 Function 指到 APEX 的 URL,认证方式选信任前置系统传头信息的那种,别让用户在 ERP 框架里再看到一次 APEX 登录页——这个体验问题被提过太多次。
我第一次做这类集成时,图省事没做导出,改崩了一个页面只能靠回忆一步步撤回去,那次之后我每次动 APEX 应用之前都会先导一份进版本库,哪怕只改一个 Label 的颜色。写到这里内容也差不多了,希望帮到你。
本文还有配套的精品资源,点击获取