OData V2 UPDATE 操作详解
在 OData V2 中,UPDATE 操作用于修改已存在的实体资源。根据 HTTP 规范,更新可以通过两种方法实现:PUT (完整替换)和 MERGE 或 PATCH(部分更新)。本文将详细讲解如何在 OData V2 服务中执行更新操作,包括客户端请求构造、SAP Gateway 后端实现、常见错误及最佳实践。
1. UPDATE 的核心概念
| 方法 | 说明 | 幂等性 | OData V2 支持 |
|---|---|---|---|
| PUT | 完整替换整个实体,未提供的字段会被重置为默认值或清空 | 是 | 是 |
| MERGE | 部分更新,只修改请求体中提供的字段,其他字段保持不变 | 否(但规范推荐视为幂等) | 是(SAP Gateway 常用) |
| PATCH | 与 MERGE 类似,但遵循 RFC 5789(PATCH 方法) | 否 | OData V3/V4 更常见,V2 部分服务支持 |
在 SAP Gateway(OData V2)中,通常使用 MERGE 作为默认的部分更新方法,也支持 PUT 。具体支持情况可以通过
$metadata或服务文档确认。
2. 客户端 UPDATE 请求示例
假设我们要更新 ZUSERSet 中 Id=2 的用户,将其 Name 改为 "Jerry",Address 改为 "上海市浦东新区"。200显示已更新成功

2.1 使用 MERGE(部分更新,推荐)
请求 URL:
http://ip:host/sap/opu/odata/SAP/ZXKJ_USER_SRV/ZUSERSet(5)
HTTP 方法 :MERGE
请求体(Atom/XML 格式):
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
<title type="text">ZUSERSet(2)</title>
<updated>2026-04-27T10:00:00Z</updated>
<category term="ZXKJ_USER_SRV.ZUSER"
scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"/>
<content type="application/xml">
<m:properties>
<d:Name>Jerry</d:Name>
<d:Address>上海市浦东新区</d:Address>
</m:properties>
</content>
</entry>
实际开发中,MERGE 更常用,因为客户端无需知道当前所有字段的值,只需发送需要更改的字段即可。
3. 成功响应
-
204 No Content:更新成功,无内容返回(最常见)。 -
200 OK:更新成功,响应体包含更新后的完整实体(某些服务配置下会返回)。
4. ABAP 后端实现(SAP Gateway)
在 SAP Gateway 中,更新操作在 DPC_EXT 类的 **UPDATE_ENTITY**方法中实现。该方法同时处理 PUT 和 MERGE。
4.1 方法签名
METHOD <entityset>_update_entity.
" 导入参数:
" it_key_tab : 主键列表
" io_data_provider : 请求数据提供者
" iv_entity_name : 实体名称
" iv_entity_set_name: 实体集名称
" io_tech_request_context : 技术请求上下文(可获取操作类型 PUT/MERGE)
4.2 实现逻辑(以 ZKJUSER 表为例)
DATA: ls_entity TYPE zkjuser, " 数据库表结构
ls_update TYPE zkjuser, " 待更新的数据
lv_id TYPE int1.
" 1. 从请求中读取更新的数据
io_data_provider->read_entry_data( IMPORTING es_data = er_entity ).
" 2. 从主键表中获取 Id
READ TABLE it_key_tab INTO DATA(ls_key) WITH KEY name = 'Id'.
IF sy-subrc = 0.
lv_id = ls_key-value.
ELSE.
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
textid = /iwbep/cx_mgw_busi_exception=>business_error
message = '未提供主键 Id'.
ENDIF.
" 3. 检查记录是否存在
SELECT SINGLE * FROM zkjuser INTO ls_entity
WHERE id = lv_id.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
textid = /iwbep/cx_mgw_busi_exception=>business_error
message = |Id = { lv_id } 的用户不存在|.
ENDIF.
* " 4. 合并数据(根据请求类型决定如何合并)
* IF io_tech_request_context->get_http_method( ) = 'PUT'.
* " PUT: 完全替换,未在请求中提供的字段需要清空
* ls_update = er_entity. " er_entity 包含请求中的全部字段
* " 注意:如果请求中某些字段缺失,它们将变为初始值
* ELSE.
* " MERGE: 部分更新,只覆盖请求中提供的字段
*
* ENDIF.
" 先用原有数据填充,再用请求数据覆盖
ls_update = ls_entity.
MOVE-CORRESPONDING er_entity TO ls_update.
" 5. 执行数据库更新
UPDATE zkjuser FROM ls_update.
IF sy-subrc = 0.
COMMIT WORK.
" 将更新后的数据返回给客户端(可选)
er_entity = ls_update.
ELSE.
ROLLBACK WORK.
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
textid = /iwbep/cx_mgw_busi_exception=>business_error
message = '更新失败,请重试'.
ENDIF.
5.3 关键点解析
-
主键处理 :主键值通常来自 URL 路径(如
ZUSERSet(5)),通过it_key_tab解析。注意请求体中的主键字段通常不需要,但若服务端强制要求,可以读取并比较。 -
客户端隔离 :务必在 WHERE 条件中加入
mandt = sy-mandt,防止误更新其他客户端的数据。 -
事务管理 :更新成功后调用
COMMIT WORK,失败则ROLLBACK WORK。 -
返回数据 :可以将更新后的实体赋值给
er_entity,这样客户端可以收到更新后的完整数据(配合状态码 200)。如果希望返回 204 无内容,可以不赋值(但需要检查服务配置)。