关于 cds-view-entities
面向 ABAP Cloud 的 ABAP CDS 视图实体语义数据建模指南,涵盖关系、注解、访问控制和 RAP 模式,并说明 SAP 数据访问的 clean core 要求。
主要功能
- 01CDS 视图实体设计
- 02关联与组合关系
- 03表达式和内置函数
- 04UI、语义与分析注解
- 05CDS 访问控制模式
- 06RAP 数据模型结构
适用场景
- 01创建独立使用或用于 RAP 的 CDS 模型
- 02建模父子实体关系
- 03添加 UI 元数据或语义注解
- 04定义数据访问限制
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ likweitan/ abap-skills --skill cds-view-entities安装后在 Claude Code 中输入/cds-view-entities确认已加载。
附带文件references/annotation-reference.md
详细介绍
CDS 视图实体
介绍如何在 ABAP Cloud 中使用 ABAP CDS(Core Data Services)视图实体构建语义数据模型。
Clean core 上下文:从已发布的 SAP CDS 视图(
I_*)中进行选择属于 Level A。直接对 SAP 数据库表进行读取属于 Level C(ATC 优先级 2);直接进行写入属于 Level D(ATC 优先级 1)。如果不存在已发布的 CDS 视图,请在未发布的 SAP 视图或表之上构建包装器 CDS 视图,并将其发布供 ABAP Cloud 使用——如果包装的对象是经典 API,则包装器属于 Level B;否则属于 Level C。请使用 CDS extends 和 CDS metadata extensions,而不要修改 SAP CDS 视图。有关级别的详细信息,请参阅abap-cloud技能。
工作流
-
确定用户的目标:
- 创建新的数据模型(独立模型或用于 RAP)
- 定义实体之间的关系(关联、组合)
- 为 UI、语义或分析添加注释
- 实现访问控制
- 在 CDS 中使用表达式、函数或参数
-
确定上下文:
- 独立 CDS 视图还是 RAP BO 数据模型
- 根实体、子实体还是投影视图
- 事务型(RAP)、分析型还是只读消费型
-
应用最佳实践:
- 使用 CDS 视图实体(v2 语法
define view entity),而非旧版 CDS 视图(define view) - 遵循命名约定(例如,接口/BO 视图使用
ZR_*,消费/投影视图使用ZC_*,复用视图使用ZI_*) - 添加适用于元数据使用者的注释(UI、OData、分析)
- 使用 CDS 视图实体(v2 语法
CDS 视图实体语法
基本视图实体
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order'
define view entity ZI_SalesOrder
as select from zsalesorder
{
key order_id as OrderId,
customer_id as CustomerId,
order_date as OrderDate,
net_amount as NetAmount,
currency_code as CurrencyCode,
status as Status,
created_by as CreatedBy,
created_at as CreatedAt,
last_changed_at as LastChangedAt
}
根视图实体(用于 RAP)
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Root'
define root view entity ZR_SalesOrder
as select from zsalesorder
composition [0..*] of ZR_SalesOrderItem as _Item
{
key order_uuid as OrderUUID,
order_id as OrderId,
customer_id as CustomerId,
order_date as OrderDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
net_amount as NetAmount,
currency_code as CurrencyCode,
status as Status,
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
@Semantics.user.localInstanceLastChangedBy: true
last_changed_by as LastChangedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_changed_at as LastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
_Item
}
子视图实体
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Item'
define view entity ZR_SalesOrderItem
as select from zsalesorder_item
association to parent ZR_SalesOrder as _Order
on $projection.OrderUUID = _Order.OrderUUID
{
key item_uuid as ItemUUID,
order_uuid as OrderUUID,
product_id as ProductId,
quantity as Quantity,
@Semantics.amount.currencyCode: 'CurrencyCode'
unit_price as UnitPrice,
currency_code as CurrencyCode,
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_changed_at as LastChangedAt,
_Order
}
投影视图(消费层)
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Projection'
@Metadata.allowExtensions: true
define view entity ZC_SalesOrder
as projection on ZR_SalesOrder
{
key OrderUUID,
OrderId,
CustomerId,
OrderDate,
NetAmount,
CurrencyCode,
Status,
CreatedBy,
CreatedAt,
LastChangedBy,
LastChangedAt,
_Item : redirected to composition child ZC_SalesOrderItem
}
关联与组合
关联类型
| 类型 | 语法 | 用例 |
|---|---|---|
| 常规关联 | association [0..1] to ZI_Customer as _Customer on ... | 独立实体(例如,主数据查找) |
| 组合 | composition [0..*] of ZR_Child as _Child | 具有生命周期依赖关系的父子实体(RAP BO 树) |
| 到父级的关联 | association to parent ZR_Parent as _Parent on ... | 组合中子级 → 父级的反向引用 |
关联语法
define view entity ZI_SalesOrder
as select from zsalesorder
association [0..1] to ZI_Customer as _Customer
on $projection.CustomerId = _Customer.CustomerId
association [0..*] to ZI_SalesOrderItem as _Item
on $projection.OrderId = _Item.OrderId
{
key order_id as OrderId,
customer_id as CustomerId,
// Expose associations — required for consumption via ABAP SQL or OData
_Customer,
_Item
}
在 ABAP SQL 中使用关联
" Path expression — triggers LEFT OUTER JOIN by default
SELECT FROM zi_salesorder
FIELDS OrderId,
\_Customer-CustomerName,
\_Item-ProductId
WHERE OrderId = @lv_order_id
INTO TABLE @DATA(lt_result).
" Filtering associations
SELECT FROM zi_salesorder
FIELDS OrderId, \_Item[ ProductId = 'PROD01' ]-Quantity
WHERE OrderId = @lv_order_id
INTO TABLE @DATA(lt_filtered).
表达式与内置函数
Cast 表达式
cast( amount as abap.dec(15,2) ) as ConvertedAmount,
cast( status as abap.char(10) ) as StatusText,
Case 表达式
// Simple CASE
case status
when 'N' then 'New'
when 'A' then 'Approved'
when 'R' then 'Rejected'
else 'Unknown'
end as StatusText,
// Searched CASE
case when net_amount > 10000 then 'High'
when net_amount > 1000 then 'Medium'
else 'Low'
end as PriorityCategory,
算术表达式
quantity * unit_price as TotalPrice,
net_amount + tax_amount as GrossAmount,
字符串函数
concat( first_name, concat( ' ', last_name ) ) as FullName,
substring( postal_code, 1, 2 ) as Region,
length( description ) as DescLength,
upper( country_code ) as CountryUpper,
日期与时间函数
dats_days_between( start_date, end_date ) as DurationDays,
dats_add_days( order_date, 30 ) as DueDate,
tstmp_current_utctimestamp() as CurrentTimestamp,
聚合表达式
define view entity ZI_OrderSummary
as select from zsalesorder
{
key customer_id as CustomerId,
count(*) as OrderCount,
sum( net_amount ) as TotalAmount,
avg( net_amount as abap.dec(15,2) ) as AvgAmount,
min( order_date ) as FirstOrderDate,
max( order_date ) as LastOrderDate
}
group by customer_id
输入参数
define view entity ZI_SalesOrderByDate
with parameters
p_date : abap.dats
as select from zsalesorder
{
key order_id as OrderId,
order_date as OrderDate,
net_amount as NetAmount
}
where order_date >= $parameters.p_date
在 ABAP SQL 中使用:
SELECT FROM zi_salesorderbydate( p_date = @lv_date )
FIELDS OrderId, OrderDate, NetAmount
INTO TABLE @DATA(lt_orders).
会话变量
$session.user as CurrentUser,
$session.client as CurrentClient,
$session.system_date as SystemDate,
$session.system_language as SystemLanguage,
联接
define view entity ZI_OrderWithCustomer
as select from zsalesorder as so
inner join zcustomer as cust
on so.customer_id = cust.customer_id
{
key so.order_id as OrderId,
cust.customer_name as CustomerName,
so.net_amount as NetAmount
}
| 联接类型 | 关键字 | 行为 |
|---|---|---|
| 内联接 | inner join | 仅包含匹配的行 |
| 左外联接 | left outer join | 左侧全部行 + 右侧匹配行 |
| 右外联接 | right outer join | 右侧全部行 + 左侧匹配行 |
| 交叉联接 | cross join | 笛卡尔积 |
注意:尽可能优先使用关联而非联接——关联会延迟解析,并支持路径表达式。
关键注释
语义注释(用于 RAP 中的托管字段)
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
@Semantics.user.localInstanceLastChangedBy: true
last_changed_by as LastChangedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
金额与货币 / 数量与单位
@Semantics.amount.currencyCode: 'CurrencyCode'
net_amount as NetAmount,
currency_code as CurrencyCode,
@Semantics.quantity.unitOfMeasure: 'QuantityUnit'
quantity as Quantity,
quantity_unit as QuantityUnit,
UI 注释(在 CDS 或元数据扩展中)
@UI.headerInfo: {
typeName: 'Sales Order',
typeNamePlural: 'Sales Orders',
title: { type: #STANDARD, value: 'OrderId' }
}
@UI.lineItem: [{ position: 10 }]
@UI.identification: [{ position: 10 }]
@UI.selectionField: [{ position: 10 }]
order_id as OrderId,
元数据扩展(推荐用于 UI 注释)
@Metadata.layer: #CUSTOMER
annotate view ZC_SalesOrder with
{
@UI.facet: [{
id: 'GeneralInfo',
type: #IDENTIFICATION_REFERENCE,
label: 'General Information',
position: 10
}]
@UI.lineItem: [{ position: 10, importance: #HIGH }]
@UI.identification: [{ position: 10 }]
OrderId;
@UI.lineItem: [{ position: 20 }]
@UI.identification: [{ position: 20 }]
@UI.selectionField: [{ position: 10 }]
CustomerId;
@UI.lineItem: [{ position: 30 }]
@UI.identification: [{ position: 30 }]
NetAmount;
}
CDS 访问控制
@EndUserText.label: 'Access Control for Sales Order'
@MappingRole: true
define role ZR_SalesOrder
{
grant select on ZR_SalesOrder
where ( CustomerId ) = aspect pfcg_auth ( Z_SO_AUTH, CUSTOMER_ID, ACTVT = '03' );
}
访问控制模式
| 模式 | 描述 |
|---|---|
pfcg_auth | 标准授权对象检查 |
inherit | 继承关联实体的限制 |
aspect user | 按当前用户限制 |
true / false | 不受限制 / 无访问权限 |
// Inherit from parent entity
define role ZR_SalesOrderItem {
grant select on ZR_SalesOrderItem
where inheriting conditions from entity ZR_SalesOrder
association _Order;
}
// User-based restriction
define role ZR_MyOrders {
grant select on ZR_SalesOrder
where CreatedBy = aspect user;
}
CDS 表实体
define table entity ztab_salesorder {
key client : abap.clnt;
key order_uuid: sysuuid_x16;
order_id : abap.numc(10);
customer : abap.char(10);
amount : abap.dec(15,2);
currency : abap.cuky(5);
}
CDS 表实体可作为经典 DDIC 数据库表的替代方案,并可在 RAP BDEF 中用作
persistent table。
RAP 的数据模型模式
典型组合树
ZR_Root (define root view entity)
├── composition [0..*] of ZR_Child1 as _Child1
└── composition [0..*] of ZR_Child2 as _Child2
└── composition [0..*] of ZR_GrandChild as _GrandChild
ZR_Child1 (association to parent ZR_Root as _Root)
ZR_Child2 (association to parent ZR_Root as _Root)
└── composition [0..*] of ZR_GrandChild as _GrandChild
ZR_GrandChild (association to parent ZR_Child2 as _Parent)
管理字段模式(托管 RAP BO)
托管 RAP BO 中的每个实体都应包含管理字段:
| 字段 | 类型 | 注解 | 用途 |
|---|---|---|---|
CreatedBy | syuname | @Semantics.user.createdBy: true | 创建者 |
CreatedAt | utclong | @Semantics.systemDateTime.createdAt: true | 创建时间 |
LastChangedBy | syuname | @Semantics.user.localInstanceLastChangedBy: true | 最后修改者 |
LocalLastChangedAt | utclong | @Semantics.systemDateTime.localInstanceLastChangedAt: true | 用于乐观并发控制的 ETag |
LastChangedAt | utclong | @Semantics.systemDateTime.lastChangedAt: true | 草稿的总 ETag |
输出格式
- 创建新视图时,提供完整的 CDS 源代码
- 包含所有相关注解
- 遵循命名约定(接口使用
ZR_*,消费使用ZC_*,复用使用ZI_*) - 始终公开消费中使用的关联
- 与安全相关时,包含访问控制定义
参考资料
CDS 的 Clean Core 级别说明
| 场景 | 级别 | 说明 |
|---|---|---|
从已发布的 SAP CDS 视图(I_*)中执行 SELECT | A | 目标状态 |
| 基于自有表创建自定义 CDS 视图 | A | 允许在同一软件组件内使用 |
从经典(指定)SAP CDS 视图中执行 SELECT | B | 在经典 ABAP 开发中可接受 |
| 基于未发布的 SAP CDS 视图创建包装 CDS 视图 | B/C | 如果包装的视图是经典 API,则为 B;否则为 C |
| 直接对 SAP 数据库表进行读取访问 | C | ATC 优先级 2——使用已发布的 CDS 视图或经典 API |
| 直接对 SAP 数据库表进行写入访问 | D | ATC 优先级 1——使用已发布的 API 或经典 API |
对 SAP CDS 视图执行 CDS extend / 元数据扩展 | A/B | 优先于修改 |
| 通过已发布的扩展包含项添加自定义字段 | A | 推荐的技术 |
| 通过未发布的扩展包含项添加自定义字段 | B | 留意是否有已发布的包含项 |
| 通过经典附加结构添加自定义字段 | C | 留意扩展包含项是否可用 |
包装 CDS 视图指南
- 如果存在合适的 SAP CDS 视图但尚未发布,请在其基础上创建自定义 CDS 视图,并将其发布以用于 ABAP Cloud 开发
- SAP 视图所引用的 CDS 视图(关联目标、值帮助视图)可能也需要各自的包装视图
- 如果完全不存在合适的 SAP CDS 视图,请直接基于数据库表创建自定义 CDS 视图
- SAP 发布等效的 CDS 视图后,弃用该包装视图
在 Cloudification Repository 中,CDS 视图归类在对象类型 STOB 下。
相关技能
- abap-sql-amdp:用于高级 ABAP SQL 查询和 AMDP 过程
- rap:用于基于 CDS 数据模型构建 RAP 业务对象
- authorization-iam:用于实现 CDS 访问控制(DCL)
- odata:用于将 CDS 视图公开为 OData 服务
- abap-cloud:用于 Clean Core 级别分类和包装 CDS 视图模式
- abap-cloud-migration:用于将直接表访问替换为已发布的 CDS 视图