本章说明团队如何长期维护和增强 Oracle APEX 应用:先识别应用定义和数据库对象等交付制品,再用导出、导入、多环境、工作副本和 Git,把每一次变更变成可比较、可测试、可回滚的发布节奏。

23 理解应用生命周期#

用户开始使用应用之后,会持续反馈缺陷和改进建议。开发团队要做的不是一次性“做完”应用,而是在一个稳定节奏中选择优先事项、拆分任务、测试结果,并把小批量、高质量的更新交付给用户。这种从开发、验证到发布的循环,就是应用开发生命周期。

有效的 Application Lifecycle Management(ALM)要求团队清楚 APEX 应用定义存放在哪里、如何在不同环境间移动、如何隔离开发中的变更,以及如何保留历史记录。APEX 应用并不是普通文件目录中的代码库,它的页面、组件和逻辑主要以元数据形式存储在 Oracle Database 中;因此,团队必须主动导出这些定义,并把导出的文本制品纳入版本管理。

学习前提:你应能进入 App Builder,知道应用 ID、工作区、解析方案(parsing schema)和目标环境;如果要复现命令行流程,还需要安装 SQLcl 和 Java,并能用应用的解析方案连接数据库。

验证目标:完成本章后,应能说清一个 APEX 发布包包含哪些制品,能从 DEV 导出应用并导入 TEST/PROD,能用环境 Banner 降低误操作风险,能用 Working Copy 隔离页面和组件变更,并能把应用导出文件提交到 Git。

23.1 识别应用制品#

APEX 应用是一组保存在 Oracle Database 中的定义。工作区为一个或多个应用提供上下文,并关联一个或多个数据库 schema。应用本身由页面、报表、表单、导航菜单、LOV(Lists of Values,值列表)、流程以及共享组件组成。这些元数据合在一起,才构成应用定义。

因为这些定义以元数据方式存放,数据字典通常只呈现当前状态。你修改一个页面或组件后,旧定义会被覆盖;APEX 不会自动为每次修改保留完整版本历史。要长期追踪变更,必须把应用导出为文本文件。APEX 可以生成一个 SQL 脚本,也可以生成一组按组件拆分的脚本。这些导出结果就是应用制品(application artifacts):可读、可比较、可版本化的应用元数据快照。

默认导出时,APEX 会生成一个名为 f<app-id>.sql 的 SQL 文件,里面包含应用的页面、组件和设置。也可以选择 Split Export(拆分导出),让每个页面或组件成为单独文件,并按页面、共享组件等目录组织。拆分格式更适合 Git:如果只修改了一个报表页,下一次导出通常只有对应页面文件发生变化,审查差异会容易得多。

这些文本制品既是安全网,也是历史记录。团队保存每个有意义的版本后,就可以在任意 APEX 实例中通过重新导入这些文件恢复过去版本。生产库保存的是用户正在使用的版本;开发环境保存的是下一版变更。把这些制品管理好,才能在版本之间切换、比较差异,并在需要时恢复早期状态。

提示:SQL 导出仍是默认方式,但如果项目需要更易读、可编辑、方便外部工具处理的表示形式,可以评估 APEXlang。APEXlang 可以用 SQLcl 的 apex validate 在无数据库连接时严格校验,也适合代码编辑器、diff 工具和 AI 编码智能体参与协作。

操作检查:在 App Builder 中打开目标应用,确认应用 ID 和关键共享组件;再执行一次普通导出和一次 Split Export。验证单文件导出得到 f<app-id>.sql,拆分导出得到按组件组织的文件树。

23.2 导出与导入应用#

导出应用,就是把数据库中的 APEX 定义抽取成文件;导入应用,则是把之前导出的文件装入某个 APEX 实例,在目标工作区创建或替换应用。两件事都可以通过 App Builder 图形界面完成,也可以用 Oracle SQL Command Line(SQLcl)自动化完成。

在 App Builder 中,打开要导出的应用,从应用操作菜单选择 Export/Import > Export。APEX 会生成 SQL 脚本;你可以下载单个文件,也可以启用 Split Export,下载包含多个组件文件的 ZIP 包。这个交互式方式适合临时备份或少量 GUI 迁移。

更可重复的方式是 SQLcl。确认 SQLcl 和 Java 已安装后,用应用工作区的解析方案连接数据库,然后运行类似命令:

sql> apex export -applicationid 12345

该命令会把应用 12345 导出到当前目录,默认生成 f12345.sql。实际项目建议加入几个选项:-split 生成按组件拆分的文件夹;-skipExportDate 省略时间戳,避免 Git 中出现无意义差异;-expOriginalIds 保留内部 ID,方便跨环境比较和刷新;如果应用使用 Supporting Objects,则加上 -expSupportingObjects

SQLcl 还可以通过 -expType 指定导出格式。默认是 APPLICATION_SOURCE,即普通 SQL 脚本;也可以指定 APEXLANG 得到更易读、可编辑的表示形式,或指定 EMBEDDED_CODE 单独抽取 SQL、PL/SQL 和 JavaScript。多个格式可以组合。下面命令应在一行中执行,选项之间用空格分隔:

sql> apex export -applicationid 12345 -split -skipExportDate -expOriginalIds -expSupportingObjects Y -expType APPLICATION_SOURCE,APEXLANG

执行后,会得到应用 SQL 格式制品目录(如 f12345/),也会得到包含 APEXlang .apx 文件的目录(通常使用应用 alias 命名)。这些文件共同表示同一版应用定义。

导入时,在 App Builder 中进入目标工作区,使用 Import 上传 .sql.zip 导出文件。APEX 会引导你确认工作区、应用 ID 等选项。如果从 DEV 导出的应用 100 要导入同一实例中的 TEST 工作区,为避免覆盖其他应用,通常需要分配新的应用 ID。APEX 会根据导出文件重建页面、组件和共享逻辑;目标应用已存在时,则按你的选择安装新副本或更新现有应用。

命令行导入 SQL 格式应用导出时,本质上是运行导出的 SQL 脚本。如果要导入到不同工作区或改用新的应用 ID,先用 APEX_APPLICATION_INSTALL 设置上下文:

begin
   apex_application_install.set_workspace('MyWorkspace');
   apex_application_install.set_application_id(56789);
end;

然后运行 f12345.sqlf12345/install.sql,应用会以新 ID 56789 安装到目标工作区。若要自动安装 Supporting Objects,可在上述设置块中加入 set_auto_install_sup_obj(true)

如果导入到同一个 APEX 实例中的另一个工作区,应用 ID 必须不同,因为同一实例内 ID 唯一。APEX_APPLICATION_INSTALL 可以生成新 ID,并调整应用 alias 和解析方案。若导入到另一个独立 APEX 实例,通常可以保留相同 ID 和 alias,只要目标实例没有冲突。无论哪种方式,导入后都要运行应用完成验证,并补齐环境专属设置或数据脚本。

提示:导出和导入只覆盖 APEX 应用定义。表、视图、PL/SQL 包等数据库对象要用 SQL 脚本、schema migration 工具或 SQLcl Projects 等方式单独迁移。发布包必须同时考虑应用元数据和支撑数据库对象。

验证检查:在 TEST 工作区导入后运行应用,确认页面能打开、共享组件可用、Supporting Objects 已按预期安装,并记录应用 ID、alias、解析方案和版本号。

23.3 使用多个环境#

大多数团队至少需要三个环境:DEV 用来开发变更,TEST 或 QA 用来验证,PROD 面向最终用户。多环境可以让开发者和测试人员自由工作,而不影响线上稳定性和真实数据。一个缺陷在 TEST 被发现,远好过让最终用户在 PROD 中遇到。

APEX 中常见的环境组织方式有两种:每个环境使用独立 APEX 实例,或者在同一个实例中使用多个工作区。

对较大或关键应用,理想做法是为每个环境准备独立 APEX 实例。例如 DEV、TEST、PROD 分别运行在不同数据库或 PDB 中,每个环境都安装 APEX。这样隔离最彻底:DEV 中的变更只有显式导出并导入后才会影响 PROD;而且每个环境可以复用相同的应用 ID、alias 和工作区名称,跟踪起来更清晰。许多 Oracle 团队会为 DEV、TEST、PROD 分别配置独立 APEX 服务。

如果无法运行多个 APEX 实例,也可以在单实例中用多个工作区做逻辑隔离。例如 MYAPP_DEV 用于开发,MYAPP_TEST 用于测试,MYAPP_PROD 用于生产,并分别映射到自己的 schema。应用共享同一个 APEX 安装,但工作区和 schema 分开。此时需要注意:同一实例中的应用 ID 和 alias 必须唯一,所以 DEV 中的应用 100 可能在 TEST 中变成 1100,在 PROD 中变成 2100。APEX 导入时支持重新分配 ID、alias 和 schema,但这种方式需要更多协调。

无论采用哪种架构,都要清晰标识环境。APEX 提供 Environment Banner,可以在 App Builder 顶部或侧边显示彩色条和文字。例如 PROD 使用醒目的红色 PRODUCTION,TEST 使用蓝色 TEST / QA,DEV 使用绿色 DEVELOPMENT。这些视觉提示能显著降低误在生产环境编辑应用的风险。

图 23-1 DEV、TEST 和 PROD 使用不同颜色与文字的 Environment Banner,帮助开发者确认当前所在环境。查看官方图片说明

典型发布流程是:开发者在 DEV 构建并自测变更;一组更新准备好后,从 DEV 导出 APEX 应用和必要数据脚本并导入 TEST;QA 或业务干系人在 TEST 验证;发现问题则回 DEV 修复并重新测试;通过后,把同一个已测试导出文件导入 PROD。这样能确保生产中运行的代码就是经过测试的代码。团队通常还会在每次发布时递增应用的 Version Number,例如 DEV 已到 2.0,而 PROD 仍在 1.9。

操作检查:为每个环境确认 URL、工作区、应用 ID、alias、解析方案、外部端点和 Banner。进入 App Builder 后,第一眼应能辨认当前是 DEV、TEST/QA 还是 PROD。

23.4 使用工作副本隔离变更#

多人同时开发同一个 APEX 应用时,需要避免互相覆盖。APEX 本身支持多人构建,不同成员可以同时编辑应用的不同部分。对于单个页面,APEX 提供页面锁定:例如你正在编辑 Page 5,可以锁定它,阻止别人同时修改。对于更大、更有风险的变更,或需要隔离开发的新功能,Working Copy(工作副本)更合适。

Working Copy 是同一工作区中从主应用克隆出来的副本,可以看作开发者自己的沙箱。开发者可以随时创建一个工作副本,用于试验、修复缺陷或开发新功能,而不会影响主应用。它类似 Git 分支的概念,但完全发生在 APEX 内部。工作副本创建时与主应用一致,之后开发者只在副本中修改。

工作副本支持并行开发。例如 Alice 创建工作副本重做报表页面,Bob 在主应用中修复缺陷。Alice 可以安全地在副本中做大范围修改;APEX 允许比较工作副本与主应用,或比较两个工作副本,查看差异。如果主应用在此期间有更新,Alice 也可以刷新自己的工作副本,把主应用的新变更拉进来。

开发完成后,可以把工作副本合并回主应用,也可以直接丢弃。合并会把副本中的变更应用到主应用定义,通常在功能或修复已经测试完成后执行。如果只是实验性工作,删除工作副本不会影响主应用。

工作副本的合并和刷新通常按页面和组件级别处理。如果两个副本修改不同页面,通常可以干净合并;如果都修改同一页面,可能产生冲突。为了降低冲突风险,在工作副本中修改某个页面前,应先在主应用锁定该页面,并写明原因。例如 Alice 修改 Page 42 前,在主应用锁定 Page 42,并备注“Working on new report UI”。这个锁既提醒别人,也阻止他们在主应用中修改同一页面。合并完成后再解锁。

除了 App Builder 内部操作,开发者也可以结合 APEXlang 使用工作副本。因为 APEXlang 可由外部工具编辑,适合代码编辑器、AI 编码智能体和 diff/merge 工具。开发者可以把工作副本导出为 APEXlang,在外部修改后再导入回 App Builder,最后使用正常 Working Copy 功能把新增或变更的页面、组件合并回主应用。

如果 App Builder 中的工作副本冲突阻止合并,可以把主应用和工作副本都导出为 APEXlang,在外部工具中手工解决冲突。解决后用 apex validate 校验更新后的主应用;校验通过后,导入合并后的 APEXlang 结果覆盖主应用,最后删除冲突的工作副本。

提示:工作副本拥有不同于主应用的应用 ID,但 APEX 知道两者有关联。如果应用逻辑依赖主应用 ID,请使用 MAIN_APP_ID,而不是 APP_ID 作为替换变量或绑定变量;它会解析为主应用的 ID。

验证检查:创建工作副本后,确认它有自己的应用 ID;修改一个页面,比较工作副本与主应用;完成测试后再合并。若使用页面锁,确认锁定说明清楚,合并后及时解锁。

23.5 使用源代码控制跟踪变更#

即使有多环境和良好的团队协作,仍然要回答一个关键问题:历史如何保留?APEX 不会自动记录完整版本历史。应用更新后,旧版本如果没有导出就会丢失。依赖记忆或手工笔记风险很高,因此大多数团队会使用 Git 做源代码控制。

Git 是事实上的开发团队版本控制标准。对 APEX 项目而言,Git 仓库保存的是导出的 APEX 应用文件、数据库脚本和相关交付文件。每次变更后,团队用一条提交消息说明改了什么。Git 会记录谁在何时改了哪些文件、增加或删除了哪些内容,以及提交消息中的原因。之后就可以回答“这个缺陷什么时候引入”“谁改过这段逻辑”“1.4 和 1.5 之间变了什么”等问题。

典型设置是使用 GitHub、GitLab、Bitbucket 或内部服务器上的远程仓库。每个团队至少有一个本地仓库副本。为了降低门槛,小团队可以先指定一个成员负责 Git:定期导出最新主应用,把文件提交并推送到共享仓库,提交消息写成简短、可理解的描述,例如 Week 34 fixes and improvements.。每个提交都是一个检查点,随着时间推移,仓库会形成按时间排列的版本记录。

提示:Working Copy 保存的是还没有准备进入主应用的进行中变更。使用工作副本的团队通常只把主应用导出到 Git。

开始使用 Git 不需要复杂流程。可以先创建一个私有仓库,把当前 APEX 应用初次导出作为第一条提交。之后在关键里程碑、测试前或固定周期做新的导出和提交。提交消息要短但有意义,能描述业务或技术变更;这些消息会逐渐成为项目历史。

Git 不只是备份。它的 diff 工具可以精确显示 APEX 导出文件中哪些行发生变化。如果有人无意修改了页面属性、授权方案、动态操作或 SQL 查询,拆分导出配合 Git diff 会更容易发现。发布时,也可以用提交历史说明这一版包含哪些变更。

源代码控制还提供可追溯性。维护人员几个月后如果想知道某个校验逻辑为什么被删除,可以查看提交历史,找到修改者、时间和提交说明,甚至关联缺陷号或业务规则更新。没有源代码控制,这类背景很容易丢失。

Git 还为自动化打开入口。团队可以把 Jenkins、GitHub Actions 等 CI 工具接入流程,自动把最新提交部署到测试环境并运行检查。即使不做完整 CI,也可以先用 SQLcl 和简单 shell 或批处理脚本自动完成两个方向:从 APEX 导出到 Git,以及从 Git 导入到目标环境。这种轻量脚本能带来可控、透明、易维护的备份和版本流程。

验证检查:把一次 Split Export 提交到 Git,修改一个页面后重新导出,再用 git diff 查看变化是否集中在预期文件中。提交消息应能解释业务原因,而不仅是“update”。

23.6 汇总生命周期建议#

SQLcl 和 Git 组合起来,就能形成一个简单有效的 APEX 应用生命周期管道:

  • 用 SQLcl 定期把 APEX 应用以及相关数据库脚本导出成文件。
  • 用 Git 提交这些导出结果,并用提交消息记录变更上下文。
  • 把经过版本管理的导出文件导入 TEST 和 PROD 等目标环境。

这个流程不需要特殊平台,只依赖你已经使用的 APEX、SQLcl 和 Git 托管服务,因此适合不同规模的团队。它遵循轻量自动化原则:把重复步骤脚本化,保留人工控制,在团队和应用规模增长后再逐步接入 CI 或更完整的 DevOps 工具。

本章的核心建议可以归纳为:理解并导出应用制品;让 DEV、TEST、PROD 清晰分离;用 Working Copy 隔离尚未完成的功能或修复;用 Git 保存每个有意义版本。这样就不会丢失变更,每个版本都可以重现,生产发布也有验证和追溯依据。

提示:更大的开发团队还可以采用以功能为中心的分支实例模式。每个开发者拥有私有 Git 克隆,并在自己独占的 branch instance 中工作。这需要更多协调,但可以让团队使用常见代码审查工具,围绕每个缺陷修复或新功能单独审查变更。

验收清单:发布前确认应用导出、数据库脚本、Supporting Objects、环境设置、版本号、验证记录和回滚方案都已纳入同一发布包或发布说明。