本章说明如何在 Oracle APEX 中集成远程数据:包括通过 REST-Enabled SQL 访问远程 Oracle 或 MySQL 数据库,使用 Web Credential 安全保存认证信息,把 REST API 建模成 REST Data Source,以及在声明式页面流程或 PL/SQL 代码中调用远程服务。
13 集成远程数据#
在 APEX 应用中,性能和可维护性通常以本地表或视图为优先。但真实业务系统经常需要读取或修改远程数据库、Fusion Applications、ORDS、OData 或其他 REST API 中的数据。APEX 的做法是把这些外部能力封装成可声明式使用的数据源、凭据和操作,让页面区域、LOV、流程和 PL/SQL 代码都能复用同一套定义。
学习本章时,应先区分三类集成:REST-Enabled SQL 面向远程数据库;REST Data Source 面向 HTTP API;APEX_WEB_SERVICE 面向需要完全手写请求逻辑或二进制载荷的特殊场景。对一组有自定义约定的 API,还可以开发 REST Source Adapter 插件,把认证、分页、过滤、排序和 DML 规则封装起来。
13.1 使用远程 Oracle 与 MySQL 数据#
REST-Enabled SQL 是 ORDS 提供的能力,允许经过认证的客户端通过 HTTPS 访问远程 Oracle 数据库或 Oracle Cloud MySQL Database System。APEX 工作区保存服务端点和认证信息后,页面区域、LOV 等组件就可以像选择本地数据源一样选择远程数据源。
- 前提:远程数据库已经启用 ORDS REST-Enabled SQL,且你拥有可用的服务 URL 与认证方式。
- 涉及位置:Workspace Utilities 中的 REST-Enabled SQL Services,以及 Page Designer 中区域或 LOV 的 Source 设置。
- 验证点:创建服务引用时,APEX 能成功认证并显示连接成功;页面区域能列出或查询远程对象。
- 预期结果:远程 Oracle 数据库支持读写;远程 MySQL 当前适合只读展示,Form 和 Interactive Grid 的自动 DML 不适用于 MySQL。
官方来源:Using Remote Oracle and MySQL Data
13.1.1 引用 REST-Enabled SQL 服务#
REST-Enabled SQL 引用是工作区级定义。创建时要提供一个清晰名称、服务端点和凭据。多数 REST-Enabled SQL 服务使用 HTTPS 上的 Basic Authentication;如果此前已定义 Web Credential,也可以直接选择已有凭据。
- 进入 Workspace Utilities,打开 REST-Enabled SQL Services。
- 启动 Create REST Enabled SQL Service 向导,填写服务名称和端点 URL。
- 输入新凭据或选择已有 Web Credential。
- 点击 Create,让 APEX 验证端点和认证信息。
官方来源:Referencing a REST-Enabled SQL Service
13.1.2 REST-Enabled SQL 服务 URL#
REST-Enabled SQL 的 URL 取决于服务部署位置。常见 ORDS 或 Autonomous AI Database 场景使用包含 ORDS schema alias 的 URL,APEX 会自动补上后续 SQL 访问路径,因此不要在配置中手工追加 /_/sql。
https://domainname/ords/your_schema_alias
如果通过 OCI Database Tools Connection 暴露 MySQL Database System,URL 中会包含 Database Tools Connection 的 OCID,并带有所在 OCI region。配置时要替换成实际 region 与 connection OCID。
https://sql.dbtools.yourregion.oci.oraclecloud.com/20201005/ords/connectionocid
官方来源:REST-Enabled SQL Service URL
13.1.3 使用 REST-Enabled SQL 服务#
服务引用创建完成后,在区域、LOV 或其他动态列表中把 Source Location 设为 REST-Enabled SQL,即可选择远程数据库中的表、SQL Statement 或 Function Body Returning SQL Query。对远程 Oracle 数据库,读写操作都可使用;对远程 MySQL,适合报表、LOV 和只读展示。
官方来源:Using a REST-Enabled SQL Service
13.2 配置安全 Web 凭据#
Web Credential 用来安全保存访问 REST-Enabled SQL 或 REST API 所需的认证信息。密钥保存后会被加密,不能再以明文取回;页面和 REST Data Source 只引用凭据的定义,而不是直接持有密钥。
| 凭据类型 | 常见用途 |
|---|---|
| Basic Authentication | 多数 REST-Enabled SQL 服务和部分 REST API。 |
| OAuth2 | Client credentials、password flow,以及配合 Social Sign-in 的 authorization code flow。 |
| OCI Native Authentication | Database Tools connection REST-Enabled SQL 服务,以及其他 OCI REST API。 |
| HTTP Header | 要求在请求头中传递 auth token 或 API key 的服务。 |
| URL Query String | 要求在 URL 参数中传递 API key 的服务。 |
| Key Pair | 推送通知和用户断言签名。 |
配置时应优先使用 Web Credential 保存认证值,不要把 token、API key 或密码做成普通 REST 参数。这样可以减少调试日志泄漏风险,并集中处理密钥轮换。
官方来源:Configuring Secure Web Credentials
13.3 把 REST API 用作数据源#
REST Data Source 把 HTTP API 封装成 APEX 可理解的数据源。每个数据源可以有一个或多个操作,对应 GET、PUT、POST、PATCH 或 DELETE 请求。运行时,APEX 使用关联的 Web Credential 认证,按配置处理分页、参数、响应映射、缓存和本地同步。
一旦定义完成,REST Data Source 可以用于区域、LOV 和动态列表。支持 DML 操作时,它还可以服务于 Form、Interactive Grid 和 APEX_EXEC 代码。Fusion Applications、ORDS 和 OData 这类有统一约定的 API 可获得更强的自动发现、过滤、排序和 DML 支持。
官方来源:Using REST APIs as Data Sources
13.3.1 使用 Simple HTTP REST Data Source#
Simple HTTP 是最通用的 REST Data Source 类型。若 API 支持 GET,Create REST Data Source 向导可先调用端点并分析 JSON 响应,自动生成初始 Data Profile;若 API 不能自动发现,也可以手工创建并补全操作和列定义。
示例 Employees API 的端点如下。它返回包含 items 数组、hasMore、limit、offset 和 count 的 JSON,其中 limit 与 offset 可用于分页。
curl -L https://example.com/ords/cloudcompanion/emp
curl -L "https://example.com/ords/cloudcompanion/emp?limit=2"
curl -L "https://example.com/ords/cloudcompanion/emp?offset=2&limit=2"
- 进入 Shared Components > REST Data Sources。
- 启动 Create REST Data Source 向导。
- 输入 endpoint URL,例如
https://example.com/ords/cloudcompanion/emp。 - 根据服务文档检查认证、分页和响应结构,再继续完成向导。
官方来源:Using a Simple HTTP REST Data Source
13.3.2 Service URL 使用 Remote Server#
同一组织的多个 REST 端点通常共享域名和公共路径,只在末尾资源路径不同。APEX 会把公共基础地址保存为 Remote Server,把尾部路径保存为 REST Data Source 的 Service URL Path。这样开发、测试、生产环境可以各自维护不同 Remote Server Base URL,而应用导入升级时仍保留各环境的目标主机。
https://example.com/ords/cloudcompanion/emp
https://example.com/ords/cloudcompanion/dept
官方来源:Service URL Uses Remote Server
13.3.3 Data Profile 把 JSON 转为行列#
每个 REST Data Source 都包含 Data Profile。它定义 Row Selector、列名、数据类型、JSON selector、主键等信息,让 APEX 可以把 JSON 响应转换为类似数据库查询的行列结果。自动发现只是起点,日期格式、主键、嵌套数组和不需要的列都应人工复核。
如果某列会用于 Form、Interactive Grid 或 DML 操作,应在 Data Profile 中启用 Primary Key。对于 HIREDATE 这类日期字段,如果自动发现的数据类型或格式掩码不准确,应在 REST Data Source 编辑页调整为正确的 DATE 与格式,例如 YYYY-MM-DD"T"HH24:MI:SS"Z"。
官方来源:Data Profile Helps Turn JSON Into Rows
13.3.4 按页检索远程数据#
Simple HTTP REST Data Source 的分页行为在编辑页的 Settings 标签中配置。选择合适的 Pagination Type 后,再配置 API 使用的参数名,例如用 limit 表示页大小、用 offset 表示跳过行数、用 hasMore 表示是否还有下一页。
配置完成后,在 Interactive Report 等支持分页的区域中选择 Source Location 为 REST Source,APEX 会自动按区域分页请求远程服务。开启 Debug Info 后,可在日志中看到类似 offset 和 limit 的实际请求参数。
官方来源:Retrieving Remote Data Page by Page
13.3.5 REST 操作与数据库动作#
REST Data Source 通过 Operation 来描述 HTTP 调用,并用 Database Action 告诉 APEX 这个操作用于 Fetch Rows、Fetch Single Row、Insert Row、Update Row 或 Delete Row。只读使用通常只需要 Fetch Rows;如果要让 Form 或 Interactive Grid 自动修改远程数据,就需要补齐单行查询、插入、更新和删除操作。
Fetch Single Row、PUT 和 DELETE 等操作常把主键值放在 URL Pattern 中。APEX 可以使用 Data Profile 主键列的区分大小写 selector 作为隐式 URL 参数,例如 :empno 或 {empno}。不要再额外定义同名 URL Pattern 参数,否则会覆盖隐式参数。
验证时可在 Debug 日志中检查 APEX 是否把页面项主键值替换到正确 URL,例如 https://example.com/ords/cloudcompanion/emp/7369。
官方来源:REST Operations and Database Actions
13.3.6 配置 Request Body Template#
Insert Row 和 Update Row 操作通常需要向 REST API 发送请求体。Request Body Template 可以混合 JSON 语法与 #NAME# 替换。列名替换通常不要手工加双引号,APEX 会按值类型转义;如果 API 需要 JSON null 而不是空字符串,可使用 #COMM!NULL#。
如果请求体需要由 PL/SQL 动态生成,可定义 Request Body 参数,例如 PAYLOAD,并把模板写成 #PAYLOAD!RAW#。!RAW 表示把参数值按原样放入请求体,避免再次 JSON 转义。
官方来源:Configuring a Request Body Template
13.3.7 使用参数传入与返回值#
REST Data Source 参数类似 PL/SQL 过程参数,可定义为 IN、OUT 或 IN/OUT。参数可以放在数据源级,也可以放在某个操作级;可以设置默认值、Required、Static,并在页面区域或 LOV 中按使用场景传入。
| 参数类型 | 方向 | 用途 |
|---|---|---|
| URL Pattern | IN | 在 URL Pattern 中用 :Name 或 {Name}。 |
| URL Query String | IN | 作为 Name=Val 放入 URL 查询字符串。 |
| HTTP Header | IN / OUT / IN/OUT | 设置或返回 HTTP header 值。 |
| Request or Response Body | IN / IN/OUT | 在模板中用 #Name#、#Name!NULL# 或 #Name!RAW#。 |
| Request or Response Body | OUT / IN/OUT | 返回整个响应体。 |
| Data Profile Column | OUT | 从单行响应中返回某个 Data Profile 列。 |
固定的 Content-Type: application/json 可用静态 HTTP Header 参数。认证和授权相关参数不要这样配置,应使用 Web Credential。
官方来源:Supply and Return Values with Parameters
13.4 声明式调用 REST 操作#
Invoke API 页面流程可以无代码调用 REST Data Source Operation。配置时把 API Type 设为 REST Source,选择数据源和操作,再把输入参数绑定到页面项、表达式或工作流变量;如果操作返回值,也可以映射回页面项。
参数也可以来自表达式。官方示例用 json_date() 将页面项日期转成 REST API 需要的 ISO 8601 JSON 值:空值返回未加引号的 null,有值返回已经带双引号的日期字符串。模板中再配合 !RAW,避免 APEX 二次转义。
create or replace function json_date(
p_item in varchar2)
return varchar2
is
l_value varchar2(255) := apex_session_state.get_timestamp(p_item);
begin
return case
when l_value is null then 'null'
else apex_string.format(
'"%s"',
to_char(
apex_session_state.get_timestamp(p_item),
apex_json.c_date_iso8601))
end;
end;
Invoke API 场景与 Form/Interactive Grid 自动 DML 不同:每个请求体值通常显式定义为 Request Body 参数,字符串替换需要自己在模板里放双引号。
{
"job" :"#P_JOB#",
"mgr" : #P_MGR!NULL#,
"sal" : #P_SAL!NULL#,
"comm" : #P_COMM!NULL#,
"empno" : #P_EMPNO!NULL#,
"ename" :"#P_ENAME#",
"deptno" : #P_DEPTNO!NULL#,
"hiredate": #P_HIREDATE!RAW#
}
官方来源:Invoking a REST Operation Declaratively
13.5 在代码中使用 REST API#
声明式 REST Data Source 是首选,因为它能复用凭据、Data Profile、参数、分页和调试能力。需要在 PL/SQL 中处理远程行或执行操作时,优先使用 APEX_EXEC;只有在需要二进制请求或响应,或必须完全控制 HTTP 细节时,再使用 APEX_WEB_SERVICE.MAKE_REST_REQUEST。
13.5.1 在代码中处理 REST Source 行#
APEX_EXEC 通过 context 处理 REST Source 行。查询时打开 query context 并循环读取行;修改时打开 DML context、添加 DML 行、设置列值、执行 DML,最后无论成功或异常都关闭 context。
官方来源:Processing REST Source Rows in Code
13.5.1.1 查询 REST Source 行#
查询时使用 OPEN_REST_SOURCE_QUERY。先用 APEX_EXEC.T_COLUMNS 声明要取回的列,用 APEX_EXEC.T_PARAMETERS 传入操作参数,再用 NEXT_ROW 循环读取。
declare
l_params apex_exec.t_parameters;
l_cols apex_exec.t_columns;
l_ctx apex_exec.t_context;
l_empno number;
l_ename varchar2(255);
l_hiredate date;
begin
apex_exec.add_column(l_cols, 'EMPNO');
apex_exec.add_column(l_cols, 'ENAME');
apex_exec.add_column(l_cols, 'HIREDATE');
apex_exec.add_parameter(
p_parameters => l_params,
p_name => 'MY_PARAM',
p_value => 1234);
l_ctx := apex_exec.open_rest_source_query(
p_static_id => 'employees_simple_http',
p_columns => l_cols,
p_parameters => l_params);
while apex_exec.next_row(l_ctx) loop
l_empno := apex_exec.get_number(l_ctx, 'EMPNO');
l_ename := apex_exec.get_varchar2(l_ctx, 'ENAME');
l_hiredate := apex_exec.get_date(l_ctx, 'HIREDATE');
end loop;
apex_exec.close(l_ctx);
exception
when others then
apex_exec.close(l_ctx);
raise;
end;
该函数还支持 order by、filter、最大行数和起始行号。注意 Simple HTTP 默认是在 APEX 数据库中取回后再排序过滤,而 Fusion Applications、ORDS、OData 类型通常会把过滤、排序和分页下推给远程 API。
官方来源:Querying REST Source Rows in Code
13.5.1.2 修改 REST Source 行#
修改时使用 OPEN_REST_SOURCE_DML_CONTEXT。列定义必须包含数据类型,至少一个列要标记为主键。然后调用 ADD_DML_ROW 指定 insert、update 或 delete,使用 SET_VALUE 填充当前行,最后 EXECUTE_DML。
官方来源:Modifying REST Source Rows in Code
13.5.1.2.1 插入 REST Source 行#
插入示例向 employees_simple_http REST Data Source 添加两名员工。重点是先声明所有要发送的列和主键,再为每一行调用 ADD_DML_ROW(...c_dml_operation_insert) 并设置列值。
apex_exec.add_dml_row(l_dml_ctx, apex_exec.c_dml_operation_insert);
apex_exec.set_value(l_dml_ctx, 'EMPNO', 1234);
apex_exec.set_value(l_dml_ctx, 'ENAME', 'LUCY');
apex_exec.set_value(l_dml_ctx, 'HIREDATE', date '2025-06-27');
apex_exec.add_dml_row(l_dml_ctx, apex_exec.c_dml_operation_insert);
apex_exec.set_value(l_dml_ctx, 'EMPNO', 1235);
apex_exec.set_value(l_dml_ctx, 'ENAME', 'LEO');
apex_exec.set_value(l_dml_ctx, 'HIREDATE', date '2025-05-14');
apex_exec.execute_dml(l_dml_ctx);
官方来源:Inserting a REST Source Row in Code
13.5.1.2.2 更新 REST Source 行#
更新示例为了支持 lost update protection,会先用另一个按主键查询的 REST Data Source 取回现有员工,再打开原数据源的 DML context,设置 row version checksum,复制现有值,然后只改需要变更的列。这样可以避免覆盖远端已经被其他用户更新的数据。
按主键返回单个对象时,响应不是 items 数组,而是一个单独 JSON object。因此这个专用 REST Data Source 的 Data Profile 不设置 Row Selector,并启用 Contains Single Row。
官方来源:Updating a REST Source Row in Code
13.5.1.2.3 删除 REST Source 行#
删除通常只需要主键列,因为 Delete Row 操作没有属性载荷。示例删除 EMPNO 为 7369 的员工。
apex_exec.add_column(
p_columns => l_cols,
p_column_name => 'EMPNO',
p_data_type => apex_exec.c_data_type_number,
p_is_primary_key => true);
l_dml_ctx := apex_exec.open_rest_source_dml_context(
p_static_id => 'employees_simple_http',
p_columns => l_cols);
apex_exec.add_dml_row(l_dml_ctx, apex_exec.c_dml_operation_delete);
apex_exec.set_value(l_dml_ctx, 'EMPNO', 7369);
apex_exec.execute_dml(l_dml_ctx);
官方来源:Deleting a REST Source Row in Code
13.5.2 以编程方式调用 REST 操作#
当 REST API 更像一个函数调用,而不是一组可查询行时,可以使用 APEX_EXEC.EXECUTE_REST_SOURCE。官方示例是单本图书下单 API:请求体包含 ISBN 和数量,响应体返回订单号和预计送达日期。
{
"isbn": "978-1565926912",
"quantity": 1
}
{
"orderNumber": 12345,
"estimatedDelivery": "2025-07-06T00:00:00"
}
官方来源:Invoking a REST Operation Programmatically
13.5.2.1 配置用于调用的 REST Source#
如果 API 没有可自动发现的 GET 方法或 OpenAPI 描述,可在向导中选择 Create REST Source Manually。手工创建后,先调整 Data Profile:删除占位列,设置响应列名、数据类型和 JSON Selector。例如订单响应可定义 ORDER_NUMBER 和 ESTIMATED_DELIVERY 两列。
随后只保留所需 POST 操作,设置名称、Static ID、Request Body Template 和请求体参数。示例操作使用 P_ISBN 与 P_QUANTITY 作为必填 Request Body 参数,并把响应中的 Data Profile Column 配成输出参数。
{
"isbn" : "#P_ISBN#",
"quantity": #P_QUANTITY#
}
如果 API 需要 Content-Type: application/json,可添加静态 Header 参数。单对象响应不需要分页,因此 Pagination Type 设为 No Pagination。
官方来源:Configuring REST Source for Invocation
13.5.2.2 在代码中调用 REST 操作#
代码调用时,先用 APEX_EXEC.T_PARAMETERS 与 ADD_PARAMETER 设置入参,再调用 EXECUTE_REST_SOURCE,最后用 GET_PARAMETER_VARCHAR2 或 GET_PARAMETER_CLOB 读取输出参数。
declare
l_params apex_exec.t_parameters;
l_new_order_number number;
l_estimated_delivery date;
begin
apex_exec.add_parameter(l_params, 'P_ISBN', '978-1565926912');
apex_exec.add_parameter(l_params, 'P_QUANTITY', 1);
apex_exec.execute_rest_source(
p_static_id => 'single_book_order',
p_operation_static_id => 'single_book_create',
p_parameters => l_params);
l_new_order_number := apex_exec.get_parameter_varchar2(
l_params, 'ORDER_NUMBER');
l_estimated_delivery := to_date(apex_exec.get_parameter_varchar2(
l_params, 'ESTIMATED_DELIVERY'));
end;
官方来源:Invoking REST Operation in Code
13.5.3 不使用 Data Source 调用 REST API#
需要发送二进制请求体或接收二进制响应时,应使用 APEX_WEB_SERVICE。普通 REST 调用也可以这样写,但分页、JSON 解析、状态码、错误处理、凭据、调试和响应映射都要自己完成。二进制请求使用 p_body_blob,二进制响应使用 MAKE_REST_REQUEST_B。
l_response_payload := apex_web_service.make_rest_request(
p_http_method => 'POST',
p_url => c_endpoint_url,
p_body => l_request.to_clob,
p_credential_static_id => null,
p_token_url => null);
如果状态码成功且响应非空,再用 JSON_OBJECT_T 解析响应并读取 orderNumber、estimatedDelivery 等属性。除非确有特殊需要,优先把 API 定义成 REST Data Source。
官方来源:Calling REST APIs Without a Data Source
13.6 Fusion Apps、ORDS 与 OData 的额外能力#
当一类 REST API 在过滤、排序、字段选择、修改和错误结构上有统一约定时,APEX 可以提供比 Simple HTTP 更强的低代码体验。内置类型包括 Oracle Cloud Applications (SaaS)、Oracle Cloud Applications (BOSS)、Oracle REST Data Services 和 OData。
- 向导可根据元数据自动发现操作和 Data Profile 列。
- 运行时可自动添加必要参数和请求体,不必手工维护常见模板。
- 过滤和排序可委托给后端系统,利用远程索引,只把需要的结果返回给 APEX。
- Oracle Cloud Applications REST Data Source 可按当前区域所需字段缩小响应负载,并把业务对象校验错误显示为 APEX 错误。
- 对 Interactive Grid 等场景,可利用 bulk API DML 能力。
验证时应查看 Debug 日志,确认过滤、排序和字段选择确实被放入远程 API 请求,而不是在本地取回后才处理。
官方来源:Extra Fusion Apps, ORDS, OData Features
13.7 REST Data Source 高级能力#
REST Data Source 不只是远程读取。它可以缓存 GET 响应,把参考数据同步到本地表,添加计算列,设置默认过滤和排序,把远程结果与本地 SQL 后处理结合,也可以通过自定义插件支持 APEX 原生不理解的 API 约定。
官方来源:Additional REST Data Source Features
13.7.1 缓存 GET 操作响应#
对选定 GET 操作启用服务器端缓存,可以减少重复 HTTP 调用。缓存可跨所有用户共享、按用户保存或限定在当前会话内;失效时间可用分钟数,也可用 DBMS_SCHEDULER calendar expression。失效值为 0 时,仅在当前页面执行期间缓存,适合多个区域共用同一 REST Data Source 的页面。
如果应用修改了会影响缓存结果的远程数据,应在相应处理逻辑中调用 APEX_EXEC.PURGE_REST_SOURCE_CACHE() 清理指定 REST Data Source 的缓存。
官方来源:Caching a GET Operation's Response
13.7.2 本地同步数据#
对于变化不频繁的远程参考数据,APEX 可以为 REST Data Source 创建并维护本地缓存表。你可以配置同步计划,也可以调用 APEX_REST_SOURCE_SYNC.SYNCHRONIZE_DATA 按需刷新。LOV 中启用 Use Synchronization Table,或区域中启用 REST Synchronization 下的 Use Local Table 后,运行时会读取本地表而不是每次远程请求。
官方来源:Synchronizing Data Locally
13.7.3 添加计算 Data Profile 列#
计算列可以用响应中已有列派生新值,并在区域、LOV 或代码中作为只读列使用。示例中响应包含 firstName 和 lastName,Data Profile 已有 FIRST_NAME 与 LAST_NAME 后,可添加 SQL Expression 类型列 FULL_NAME。
FIRST_NAME || ' ' || LAST_NAME
其他计算列类型还包括 Lookup 和 SQL Query (return single value)。使用时要注意:计算发生在 APEX 处理远程响应之后,不会改变远程 API 返回的原始数据。
官方来源:Adding Computed Data Profile Columns
13.7.4 配置默认过滤和排序#
Oracle Cloud Applications、ORDS 和 OData 等 REST Data Source 类型支持各自的声明式外部过滤和排序语法。区域或 LOV 启用 External Filter and Order By 后,可写入后端服务能理解的表达式。
StatusCode = 'OPEN' and WinProb >= 75
WinProb:desc,PrimaryContactPartyName:asc
配置前必须查阅目标服务文档,因为 Fusion Applications、ORDS 和 OData 对过滤、排序语法的要求不同。验证时检查请求 URL 或请求体,确认表达式被传给远程服务。
官方来源:Configuring Default Filtering and Sorting
13.7.5 过滤、排序、连接或聚合远程数据#
当远程 API 不支持所需的过滤、排序、连接或聚合时,可使用 Local Post-Processing 在 APEX 数据库中处理已取回的 JSON 行列结果。这种方式灵活,但远程数据仍然要先取回本地,因此不适合超大结果集。
官方来源:Filter, Sort, Join, or Aggregate Remote Data
13.7.5.1 理解 JSON_TABLE 查询#
APEX 使用 Data Profile 信息动态生成 JSON_TABLE() 查询,把响应 CLOB 映射为关系型结果。以 Employees API 为例,Data Profile 中的 EMPNO、ENAME、HIREDATE 会变成 JSON path 列,再按声明的数据类型转换。
select "EMPNO", "ENAME", "HIREDATE"
from (
select to_number("EMPNO") as "EMPNO",
"ENAME" as "ENAME",
to_date("HIREDATE", 'YYYY-MM-DD"T"HH24:MI:SS"Z"') as "HIREDATE"
from json_table(
response_payload format json,
'$."items"[*]'
columns (
"EMPNO" varchar2(4000) path '$."empno"',
"ENAME" varchar2(4000) path '$."ename"',
"HIREDATE" varchar2(4000) path '$."hiredate"'
)
)
)
理解这个结构有助于判断后续 WHERE、ORDER BY 或自定义 SQL 能引用哪些列,以及类型转换发生在哪里。
官方来源:Understanding the JSON_TABLE Query
13.7.5.2 本地过滤与排序远程数据#
把区域或 LOV 的 Local Post-Processing 设为 Where/Order By Clause 后,可以添加本地 WHERE 和 ORDER BY。这些子句追加在 APEX 生成的最外层 SQL 上,因此只能引用最外层 SELECT list 中已有的列;若需要按未显示列过滤或排序,应把该列加入区域并隐藏。
HIREDATE > DATE '1981-10-31'
HIREDATE DESC
这会先从远程 API 取得原始 Employees 数据,再在 APEX 数据库中过滤 1981 年 10 月之后入职的员工,并按入职日期倒序显示。
官方来源:Locally Filtering and Sorting Remote Data
13.7.5.3 使用涉及远程数据的自定义 SQL#
把 Local Post-Processing 设为 SQL Query 后,可通过 #APEX$SOURCE_DATA# 内联视图访问 APEX 原本生成的远程数据结果。初始 SQL 通常只是原样选择列,你可以加入本地表连接、表达式、聚合或重命名。
select r.EMPNO,
r.ENAME,
r.HIREDATE,
c.NAME as COURSE_NAME,
t.STATUS
from #APEX$SOURCE_DATA# r
join EMP_TRAINING t
on r.EMPNO = t.EMPNO
join EMP_COURSE c
on t.COURSE_ID = c.ID
where t.STATUS in ('COMPLETED','CERTIFIED')
要求只有两个:SQL 必须语法正确,并且包含 #APEX$SOURCE_DATA#。需要动态 SQL 时,可以改用 PL/SQL Function Body Returning SQL Query。
官方来源:Using Custom SQL Involving Remote Data
13.7.6 创建自定义 REST Source 插件#
如果某组 API 有固定约定,但 APEX 原生类型不支持,可以创建 REST Data Source 插件。插件会成为 Create REST Data Source 向导中的新类型,让团队复用你封装的发现、分页、过滤、排序、DML 或执行逻辑。
| 契约过程 | 作用 |
|---|---|
| Capabilities | 声明是否支持服务器端过滤、排序和分页。 |
| Discover | 根据服务元数据发现操作、参数和 Data Profile 列。 |
| Fetch | 检索数据,并可支持服务器端过滤、排序和分页。 |
| DML | 保存 Form、Interactive Grid 或 APEX_EXEC 产生的数据变更。 |
| Execute | 处理 Invoke API 或 EXECUTE_REST_SOURCE 调用场景。 |
可参考 Oracle APEX GitHub 仓库中对应版本的 plugins/rest-source 示例。插件属于平台级扩展,应配套版本管理、测试和 API 契约文档。
官方来源:Creating a Custom REST Source Plug-in
13.8 调试 REST API 错误#
REST API 运行时可能返回 HTTP 400、401、403、404 或 500。APEX 内置 Debug tracing 能显示请求 URL、方法、参数、请求体、响应体、状态码和错误栈,是定位 REST Data Source 问题的首选工具。
官方示例中,Interactive Report 使用 Fusion Applications Sales Opportunities REST Data Source。用户在 Actions > Data > Sort 中把 Assignment Mode 设为第三个排序列,点击 Apply 后收到 ORA-20999: REST Data Source returned an HTTP error: HTTP 400: Bad request。
从 App Builder 运行应用时,开发者工具栏默认显示在页面底部。选择 Debug > Current Debug Level > Info 后,当前页会刷新并开始生成 debug trace。之后每个页面请求或 Ajax 刷新都会产生对应 trace。选择 Debug > View Debug 可打开最近 trace 列表。
Debug 详情页的时间线用红点标识错误位置。Info 级别通常足够定位多数问题;必要时再提高 Debug 级别。点击红点或相关日志行,找到第一个远程调用错误,而不是只看最终页面异常。
示例的响应负载说明 AssignmentMode 不可排序。向上查找前一个 REST API 调用,可看到 APEX 把用户排序配置委托成远程 API 的 orderBy 查询字符串。
making GET request to https://server/crmRestApi/resources/11.13.18.05/opportunities
?onlyData=true
&fields=TargetPartyName,Revenue,CurrencyCode,WinProb,PrimaryContactPartyName,...
&orderBy=WinProb:desc,Revenue:desc,AssignmentMode:asc
&limit=500
修复方法是回到 Page Designer,选中 Interactive Report 的 ASSIGNMENTMODE 列,关闭 Enable Users To > Sort。这样最终用户在排序对话框中不再能选择该不可排序列。
- 验证点:重新运行页面,打开 Actions > Data > Sort,确认 Assignment Mode 不在可排序列列表中。
- 预期结果:Interactive Report 可正常刷新,不再触发远程服务的 HTTP 400。
- 后续处理:诊断完成后关闭 Debug tracing,避免继续产生不必要的调试日志。