
简介《Oracle APEX深入浅出》面向Oracle APEX初中级开发者与企业级Web应用实施人员围绕从环境搭建到应用落地的完整链路展开适合希望系统掌握页面设计、控件使用、报表图表与部署集成的读者。压缩包内含1个doc文档约8.11MB以章节化说明组织内容便于按目录通读或针对具体模块检索。文档覆盖APEX简介、账户与权限管理、开发概要、页面布局美化、常用控件、Report增删改、文件上传下载、CSS与JS应用、图表报表、插件、部署调试技巧并延伸至EBS中配置APEX页面调用等集成场景。已有1593人学习可作为项目实践中的操作参照与查漏补缺材料帮助读者理解APEX开发流程、常见组件用法和报表实现思路提升企业应用搭建效率。1. 一套长在数据库里的平台Oracle APEX 到底替谁省事第一次在浏览器里点开一个用 Oracle APEX 搭出来的内部审批页我以为是某个前端团队做的成品直到同事把地址栏里的http://server:8888/apex指给我看说这套东西没有打包命令、没有 Node 依赖、没有前端工程目录。Oracle APEX 的深入开发之所以和常规 Web 项目不是一回事差别全在这一句上页面不是文件是数据库里的行你点提交本质是一次 PL/SQL 调用结果通过sys.htp.p吐回 HTML。这套开发指南资料围绕的正是这条链路——从 11g 环境里装 APEX 4.2、管三层账户、配 Grid Layout、写校验与动态动作一直做到 Tabular Form 的多行增删改。适合手里有 Oracle 库、要做内部管理系统的后端和 ERP 方向开发者只想做公网 C 端页面的看完第一章就可以撤。2. 装 APEX 4.2从清掉自带旧版到跑通 8888 端口2.1 先定版本与运行形态别急着解压装之前要先把两件事定死数据库版本和 HTTP 入口方式。11g R1/R2 自带 PL/SQL 网关EPG起一个监听就能直接对外提供 HTTP 服务适合单人开发和内网演示10g 没有这个便利必须挂 mod_plsql 或者独立的监听器组件。资料里的做法是嵌入式网关我当时也走了这条理由很实在不用额外装中间件不用配连接池虚拟机挂起再恢复端口还在。版本上要留个心眼。11g 出厂自带的 APEX 是 3.x页面渲染、Grid Layout 的粒度、动态动作的能力都和 4.x 有差距尤其是 4.1 之前 Region 的 Column 属性是全局生效的想给某个子区域单独排布根本没入口4.2 才把 Grid Layout 下放到每个对象。所以第一步不是装是卸——把 3.x 彻底清掉再装 4.2混着装的结果后面在登录环节会还给你。我一般会在动手前把整台机器的状态固定下来操作系统版本、库的 SID、$ORACLE_HOME路径、要用的表空间名全部写在一张便签上。装 APEX 的坑十有八九不是安装脚本报错而是路径、SID、schema 名对不上回头改的时候找不到原始值。2.2 卸旧版、清遗留用户、建独立表空间先看库里到底有几个 APEX 相关 schema。这一步别省后面排查登录问题全靠它。-- 以 sys as sysdba 登录后执行 -- 1) 看当前残留的 APEX schema3.x 一般是 APEX_0302004.2 是 APEX_040200 SELECT username, account_status FROM dba_users WHERE username LIKE APEX_% ORDER BY username; -- 2) 跑卸载脚本脚本在 APEX 安装包根目录必须先解压出来 -- 在 sqlplus 里执行apxremov.sql -- 脚本会依次删除 APEX_030200 / APEX_PUBLIC_USER 等对象跑完再查一次上面的 SQL 确认干净apxremov.sql执行完不代表干净。资料里踩过的坑是旧版本的用户还在新版本装上去之后登录授权链路仍然指向旧 schema表现就是 ADMIN 密码改十遍也进不去。所以卸载后要再手工确认一遍把APEX_03xxxx这一批用户清掉。注意只清旧版本号的那一批APEX_040200是待会儿要用的误删就得整个重装没有后悔药。表空间建议单独建。APEX 自己的元数据加上静态资源几百兆起步和业务数据挤在 SYSAUX 里日后扩容、备份、迁移都会互相牵制。-- 独立表空间初始 100M每次扩 100M封顶 1G够一个中小型内部系统用 CREATE TABLESPACE apex01 DATAFILE /d01/oracle/ORCL/db/apps_st/data/apex01.dbf SIZE 100M AUTOEXTEND ON NEXT 100M MAXSIZE 1000M;路径里的ORCL换成你实际的 SID/d01/...这段换成库的 datafile 目录。AUTOEXTEND ON一定要开APEX 装中文语言包、传插件、存静态文件都会写库空间不够时的报错信息非常含糊先给足余量省事。2.3 解压、跑安装脚本、解锁账户安装包上传到$ORACLE_HOME下解压保持目录干净不要把几个版本混在一个文件夹里。# 解压到 ORACLE_HOME 下产物是一个 apex 目录 unzip -o apex_4.2.5.zip -d $ORACLE_HOME/ # 确认关键脚本都在缺一个后面就卡住 cd $ORACLE_HOME/apex ls apexins.sql apxchpwd.sql apex_epg_config.sql apxremov.sql apxlang.sql然后进 sqlplus 跑安装脚本。四个参数的含义是APEX 元数据表空间、静态文件表空间、临时表空间、静态资源虚拟路径。-- 用 sys as sysdba 执行 -- 参数顺序元数据表空间、静态文件表空间、临时表空间、图片虚拟目录 apexins.sql APEX01 APEX01 TEMP /i//i/是静态资源的 URL 前缀装完浏览器请求的图片、CSS 都走http://server:8888/i/...。这个值改起来麻烦一次定好。脚本跑完会打印 ADMIN 的临时密码和访问地址屏幕滚得快建议开 sqlplus 的 spool 把输出存下来。接着改 ADMIN 密码并解锁几个内置账户。-- ADMIN 是 APEX 平台的超级管理员不是数据库用户密码策略要求大小写数字特殊字符 apxchpwd.sql -- ANONYMOUS 用于公开页面APEX_PUBLIC_USER 是网关连库的入口两个都要解锁 ALTER USER ANONYMOUS ACCOUNT UNLOCK; ALTER USER APEX_PUBLIC_USER ACCOUNT UNLOCK;APEX_PUBLIC_USER锁着的时候网关能起来页面也能打开但一登录就报认证失败现象和密码错几乎一样很难第一眼分辨。装完先查一次account_status确认是 OPEN 再往下走。中文语言包用apxlang.sql按提示选编号非必须但给业务方看的时候省解释。2.4 配嵌入式 PL/SQL 网关把 8888 端口跑通网关配置脚本的参数是 APEX 安装目录的绝对路径它要做的事是把这个目录映射成数据库里的虚拟目录。-- 配置 EPG参数是解压后的 APEX 目录 apex_epg_config.sql /d01/oracle/product/11.2.0/dbhome_1/apex -- 设置 HTTP 端口8888 是资料里用的值换成别的也可以别和已有服务冲突 EXEC DBMS_XDB.SETHTTPPORT(8888); -- 确认端口真的生效了 SELECT DBMS_XDB.GETHTTPPORT FROM DUAL;配完访问http://server:8888/apex/apex_admin。如果吃到 404先别怀疑安装失败八成是网关没启动或者路径没注册上重启一次监听再试。-- 重新注册服务并检查 EPG 状态 ALTER SYSTEM REGISTER; -- 看虚拟目录有没有挂上 SELECT PATH, NAME FROM DBA_XML_TABLES WHERE PATH LIKE /apex%;还有一个容易被忽略的授权APEX schema 需要能对外建连接否则后续调 Web Service、发邮件、读外部文件全部失败报错还和网络无关。-- 给 APEX schema 放开出站连接权限 BEGIN DBMS_NETWORK_ACL_ADMIN.CREATE_ACL( acl apex_acl.xml, description APEX outbound, principal APEX_040200, is_grant TRUE, privilege connect); DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL(acl apex_acl.xml, host *); COMMIT; END; /到这一步apex_admin能打开、ADMIN 能登录环境就算立住了。整个过程真正的技术含量不高难的是每一步都能自己验证——端口、账户状态、schema 清单这三个查一遍问题基本定位到大半。3. 三层账户与 Schema 绑定ADMIN、Workspace 管理员、开发者3.1 权限层级与 Schema 绑定的含义APEX 的账户体系是三层理解错了会在建工作空间时反复返工。最高层是 ADMIN只活在apex_admin这个入口里职责是建、删、改 Workspace 和 Workspace 管理员它本身不写业务代码。中间层是 Workspace 的管理员负责在自己这个工作空间里建开发账号和终端用户账号。最下面是开发者和应用最终用户前者进 App Builder 开发后者只能用应用。关键概念是 Schema 绑定一个 Workspace 对应数据库里的一个 Schema页面、视图、PL/SQL 全部以这个 Schema 的身份执行。资料里把 Workspace 类比成工程、Application 类比成 Form这个说法挺好用但要注意补一句——工程里所有对象的权限就是那个 Schema 的权限没有额外的隔离层。所以建 Workspace 时选的 Schema必须提前在库里建好并且有建表、建视图、建存储过程的权限。同理一个 Application 相当于一个 Form页面相当于 Form 里的窗口。这套映射关系对做过 ERP 二次开发的人几乎没有理解成本对纯 Web 背景的人则要花点时间接受这里没有后端服务只有 Schema。3.2 用 ADMIN 建 Workspace三个输入项各自是什么从apex_admin登录后进 Workspace 管理建一个新空间。界面上要填的核心就三样手工填和跑 API 等价。-- 以 ADMIN 身份连库后执行效果等于在 apex_admin 里点一遍创建向导 BEGIN APEX_UTIL.CREATE_WORKSPACE( p_workspace DEMO_WS, -- 工作空间名登录时的租户标识 p_primary_schema APPS, -- 绑定的数据库 schema业务对象都在这 p_workspace_id NULL); -- 传 NULL 由系统分配 END; /p_workspace这个名字会出现在登录框里取短一点、别带空格。p_primary_schema必须已经存在大小写按库里dba_users查出来的为准写成小写会绑不上。如果业务数据分散在多个 SchemaAPEX 支持一个 Workspace 挂多个 Schema但主 Schema 只有一个跨 Schema 访问要靠授权和同义词别指望它像分库分表那样自动路由。创建过程中会顺带指定这个 Workspace 的管理员账号。这个账号不是数据库用户只存在 APEX 的元数据表里密码策略由 APEX 自己管。3.3 在 Workspace 内建开发用户与终端用户用刚才建的管理员账号登录http://server:8888/apex进管理菜单建两类用户。-- 建一个开发账号能进 App Builder BEGIN APEX_UTIL.CREATE_USER( p_user_name DEV01, p_web_password Init#2024pwd, p_email_address dev01example.com, -- 示例域名实际填可收信地址 p_default_schema APPS); END; / -- 建一个终端用户只能跑应用、不能改页面 BEGIN APEX_UTIL.CREATE_USER( p_user_name ENDUSER01, p_web_password Init#2024pwd, p_is_application_user Y); END; /p_default_schema决定这个开发者进 App Builder 之后默认在哪个 Schema 下建对象不填的话每次建表都要手选很容易建错地方。终端用户那个标记要记得打否则他能看见 App Builder 入口虽然未必有权限改但演示给客户看的时候很尴尬。账户这块最常见的翻车不是建不出来是建出来登录不上。原因通常有两个Workspace 状态是锁定的或者账号密码过期被要求重置而邮件发不出去测试环境没配邮件服务器。前一个在 ADMIN 里能看到状态后一个只能手工重置密码。3.4 应用级认证机制怎么选Application 创建时会让你选认证方式APEX 自带七种常用的就两种。一种走 APEX 自己的账户表用户必须用 APEX 账号密码登录。适合内部工具、人少、不接统一认证的场景配起来零成本。另一种走 HTTP Header Variable由前置的网关或者应用服务把身份塞进请求头APEX 只负责读。适合已经有统一身份入口的场景比如从某个 ERP 套件里点链接跳进来用户的身份在上游已经确定。资料里与 ERP 套件结合的部分走的就是后一条路上游系统负责认证APEX 只认请求头里的用户名。这条路的好处是用户不用记第二套密码坏处是请求头一旦被伪造APEX 这边没有任何反制手段只能靠网络层限制来源。所以配 Header 认证的应用我一般要求它在内网并且网关只接受来自上游系统 IP 的请求。Page 0 是另一个值得单独说的地方。它是整个 Application 的全局页放的变量、常量、全局逻辑对所有页面生效页面上这些区域不渲染等价于 HTML 里的 hidden。把用户 ID、当前角色、公共日期这类东西放 Page 0比在每个页面重复写一遍计算逻辑干净得多。4. Grid Layout 与控件联动一份能抄的 Page2 配置4.1 三个属性对应 TABLE、TR、TDAPEX 的页面布局说到底还是 DIV TABLE 的组合主题定了之后CSS 基本不用动你只需要决定某个区域落在哪个格子。Body1 到 Body3、Position1 到 Position4 这几个槽位开放给开发者用其中 Body 区域在 HTML 里渲染成TABLEPosition 区域渲染成DIV。真正要理解的是区域属性里的三个开关对应关系如下表。属性对应 HTML作用Start New Grid新的table当前对象另起一张表不继承上一张表的列结构Start New Rowtr当前对象换行落到新的一行Columntd的序号当前对象在这一行里的第几列三者的优先级是Grid 先决定用哪张表Row 决定换不换行Column 决定落在第几格。资料里 4.1 版本的痛点是 Column 全局生效改一个区域会连带影响别的区域4.2 之后每个对象都能单独配复杂布局才有了可操作性。配布局的时候先在纸上画格子标好每个区域属于哪张表、第几行、第几列再回来填三个下拉框比在界面上反复预览快得多。4.2 六个 Region 的网格参数表按资料里的例子Page2 上放六个 HTML Region父区域统一选 Page Template Body(1)分成 Search1、Search2、Search3 和 New Search1、New Search2、New Search3 两组。第一组占一行三列第二组重新起表占一行三列。参数这样填RegionStart New GridStart New RowColumnSearch1YesYes1Search2NoNo2Search3NoYes2New Search1YesYes1New Search2NoNo2New Search3NoNo3Search3 那行值得单独看一眼Start New Grid No说明它和 Search1、Search2 在同一张表里Start New Row Yes说明它换行了Column 2说明它落在第二列。最终效果是第一行两列、第二行从第二列开始视觉上形成一个错位这种布局用固定模板做不到只能靠这三个属性拼。第二组从 New Search1 开始Start New Grid Yes等于另起一张表所以它不受第一组列宽的影响。如果后面还要插子区域在 New Search2 里继续建父区域指到 New Search2Column从 1 重新算起。4.3 控件属性、校验条件与 PL/SQL 表达式APEX 控件的属性面板看着长常用的就那几个宽度、对齐方式、是否必输、只读条件、默认值、格式掩码。真正拉开差距的是条件表达式——Condition Type可以让控件在满足条件时才显示、才只读不用写一行 JS。实例是这个页面上有文本框 P1_X_TEXT 和按钮 SEARCH2_BTN要求点按钮时校验 P1_X_TEXT 是否为空但校验只在演示表里存在user_id 123这条记录时才触发。先建表。-- 演示表字段按资料里的结构精简 CREATE TABLE demo_test ( user_id VARCHAR2(50), resp_id VARCHAR2(50), login_name VARCHAR2(50), login_count NUMBER, login_date DATE ); INSERT INTO demo_test VALUES (123, NULL, NULL, NULL, NULL); COMMIT;然后在 P1_X_TEXT 上右键建校验四组参数这样配When Button Pressed选 SEARCH2_BTNCondition Type选 ExistsSQL 查询至少返回一行条件表达式写select 1 from demo_test t where t.user_id 123校验类型选 Function Returning Boolean表达式如下。-- 返回 true 表示通过false 表示拦截并显示错误信息 begin if :P1_X_TEXT is not null then return true; end if; return false; end;Condition Type这一层是很多人漏掉的。Exists 不满足时校验根本不执行页面直接提交表现就是有时候拦有时候不拦看起来像玄学实际是条件没设。错误信息位置选 Inline with Field 或 Notification 都行前者贴着字段显示后者在页面顶部弹一条内部系统我倾向 Inline用户不用回头找。4.4 动态动作值变化隐藏区域、页面加载写当天日期动态动作是 APEX 里最像前端框架的部分但不用写 JS定义事件、条件、动作三步执行 PL/SQL 时相当于一次无刷新请求。第一个需求P2_X_TEXT 的值不为空时区域 New Search2 隐藏为空时显示。配置如下——名称随意Condition Type选 AlwaysWhen Event选 ChangeSelection Type选 Item(s)Item(s)填 P2_X_TEXTWhen Condition选 is not nullTrue Actions 里加一个 Hide Region选 New Search2False Actions 里加一个 Show Region。这里有个细节Change 事件的触发时机是值改变并且控件失焦不是每敲一个字符就触发。想让输入即响应得换 Key Release 事件但要自己加节流否则每敲一下发一次请求。第二个需求页面加载完把日期控件 DATA 的初始值设成当天。这个走 Page Load 事件。配置项值Condition TypeAlwaysWhen EventPage LoadTrue ActionSet ValueSet TypeSQL StatementSQL Statementselect sysdate from dualAffected ElementsItem(s) DATA用sysdate而不是让用户手选看着是小事实际能省掉大量日期格式填错的咨询。数据库时区和浏览器时区不一致时日期控件会有偏移这种时候要在 Application 级别统一时区设置而不是在每个页面上打补丁。把这三块布局、校验、动态动作跑通一个可用的查询录入页面就成型了剩下的是往里面加字段和业务逻辑。5. 避坑排查五条血泪记录与上线前自检5.1 五个典型现象、原因与解决第一条ADMIN 密码反复改登录始终失败。现象是apex_admin页面正常打开输入账号密码后跳回登录页或者提示认证失败改密码、重启库都没用。原因是库里残留了旧版本 APEX 的用户登录校验链路指向的还是旧 schema新装的这套没被真正调用。解决办法是查dba_users里APEX_%的清单把APEX_03xxxx这类旧版本用户全部 drop 掉再重建 ADMIN 密码。判断依据很简单如果库里同时存在两个版本的 APEX schema基本就是这个问题。第二条顺手 drop 了 APEX_040200整个平台不可用。这条是和上一条连着的清旧用户时手滑把当前版本也删了。现象是所有页面 500日志里全是对象不存在的错误。APEX 的引擎本身就是一堆 PL/SQL 对象schema 没了等于整个平台没了没有修复命令只能重新跑一遍安装。我的做法是清用户之前先把清单贴到文本编辑器里逐个核对版本号再执行绝不在一行里连着写多个 drop。第三条区域布局怎么调都错位。现象是按预想的行列配了 Column预览出来还是挤在一起或者换行位置不对。原因多半是把Start New Grid理解成了新的一行。它实际是新的一张表一旦为 Yes后面的 Column 计数从头开始和上一张表没有关系。解决方式是在纸上先画表格边界明确哪几个区域共享一张表同一张表里的区域Start New Grid全填 No只有第一个填 Yes。第四条动态动作的 Change 事件不触发。现象是输入框改了值隐藏的区域没反应。三个原因按概率排一是Selection Type选成了 Region 或者 jQuery Selector没选到具体的 Item二是When Condition里写了is not null但没配 True/False 两套动作只配了 True三是值是通过动态动作赋值进去的程序改值不会触发 Change 事件只有用户手动输入才会。前两个改配置第三个得换事件类型或者在赋值动作后手动触发。第五条Tabular Form 点提交数据没更新也没报错。现象是勾了行、改了值、点 Apply Change页面刷新回来还是旧数据。原因通常是 Process 的执行点不对——放在 Before Header 或者 On Submit 之前数据还没写进会话就执行了或者是 Process 类型没设成 Tabular Form取不到多行的数组。解决办法是把执行点调到 After Submit类型选 Tabular Form并且在 Process 里显式遍历APEX_APPLICATION.G_F0x数组而不是把它当成单行表单处理。5.2 改配置之前先跑一遍的自检顺序装完、配完、改完别急着给业务方看按这个顺序过一遍先查 schema 清单确认没有版本混装再查账户状态确认APEX_PUBLIC_USER和ANONYMOUS都是 OPEN然后查 HTTP 端口是否生效最后用开发者账号实际登录一次 App Builder。这四步里任何一步跳过后面出的问题都会被误判成别的原因。代码层面的自检更简单页面上的每个校验把条件表达式单独拿到 sqlplus 里跑一遍每个动态动作先在浏览器控制台看请求有没有发出去。APEX 的调试信息不算友好但它至少不骗人——请求没发就是事件没绑上请求发了报错错误码会写在响应里。6. Tabular Form 客户化视图建模与 Process 顺序的一个技巧Tabular Form 是最容易被低估的组件。它看起来只是个能编辑的表格实际是一张以视图为数据源的批量表单多行更新、行级校验、提交后客户化逻辑全靠它。我踩过最久的一个坑是把数据源直接建成表结果某些行改完保存后莫名其妙回滚——原因是视图里缺一个稳定行标识APEX 认不出哪一行对应哪条记录。做法是先建视图把主键或者rowid显式带出来。-- 视图作为 Tabular Form 的数据源rowid 给组件一个稳定的行标识 CREATE OR REPLACE VIEW v_demo_class_user AS SELECT u.user_id, u.login_name, u.class_id, c.class_name, u.rowid AS row_id FROM demo_user u, demo_class c WHERE c.class_id u.class_id;页面上的 Tabular Form 指到这个视图勾选列、用户名列、班级名列各占一个f##编号。然后给 Apply Change 按钮建 Process类型选 Tabular Form执行点必须在 After Submit。-- 逐行校验班级名是否存在存在才更新不存在直接给行级错误 DECLARE l_cnt NUMBER; BEGIN FOR i IN 1 .. APEX_APPLICATION.G_F01.COUNT LOOP SELECT COUNT(*) INTO l_cnt FROM demo_class WHERE class_name APEX_APPLICATION.G_F04(i); IF l_cnt 0 THEN APEX_ERROR.ADD_ERROR( p_message 班级不存在 || APEX_APPLICATION.G_F04(i), p_display_location APEX_ERROR.C_INLINE_IN_NOTIFICATION); RETURN; -- 有任何一行不合法就整体回滚不做半截提交 END IF; UPDATE demo_user SET class_id (SELECT class_id FROM demo_class WHERE class_name APEX_APPLICATION.G_F04(i)) WHERE user_id APEX_APPLICATION.G_F03(i); END LOOP; END;这段代码里三个地方值得说清楚。G_F01是勾选列G_F03、G_F04是用户名和班级名编号必须和页面上列定义里的f##一一对应改页面列顺序就要同步改代码否则取到的是错位的值。APEX_ERROR.ADD_ERROR会把错误挂到对应行上用户能直接看到哪一行有问题比顶部弹一条笼统的错误有用得多。RETURN的位置决定了提交语义放在循环里是有错就整体放弃放在循环外则会先更新完合法行再报错前者更适合有约束关系的表。验证方法很土但有效先在视图里手工改一条不存在的班级名点提交应该看到行级报错且数据没变再改成合法值提交后回表查一次class_id是否真的跟着变了。这两步都过Process 才算真的通了。从那以后我每次给 Tabular Form 建数据源都强制先确认视图里有没有稳定行标识再确认 Process 的执行点最后才去写业务逻辑。顺序错了后面调的每一个小时都是白花的。希望帮到你。本文还有配套的精品资源点击获取