mcpField
C
cds-view-entities

cds-view-entities

github.com/likweitan数据与数据库7297 次安装2 周前更新
代码仓库

关于 cds-view-entities

面向 ABAP Cloud 的 ABAP CDS 视图实体语义数据建模指南,涵盖关系、注解、访问控制和 RAP 模式,并说明 SAP 数据访问的 clean core 要求。

主要功能

  1. 01CDS 视图实体设计
  2. 02关联与组合关系
  3. 03表达式和内置函数
  4. 04UI、语义与分析注解
  5. 05CDS 访问控制模式
  6. 06RAP 数据模型结构

适用场景

  1. 01创建独立使用或用于 RAP 的 CDS 模型
  2. 02建模父子实体关系
  3. 03添加 UI 元数据或语义注解
  4. 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 技能。

工作流

  1. 确定用户的目标:

    • 创建新的数据模型(独立模型或用于 RAP)
    • 定义实体之间的关系(关联、组合)
    • 为 UI、语义或分析添加注释
    • 实现访问控制
    • 在 CDS 中使用表达式、函数或参数
  2. 确定上下文:

    • 独立 CDS 视图还是 RAP BO 数据模型
    • 根实体、子实体还是投影视图
    • 事务型(RAP)、分析型还是只读消费型
  3. 应用最佳实践:

    • 使用 CDS 视图实体(v2 语法 define view entity),而非旧版 CDS 视图(define view)
    • 遵循命名约定(例如,接口/BO 视图使用 ZR_*,消费/投影视图使用 ZC_*,复用视图使用 ZI_*)
    • 添加适用于元数据使用者的注释(UI、OData、分析)

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 中的每个实体都应包含管理字段:

字段类型注解用途
CreatedBysyuname@Semantics.user.createdBy: true创建者
CreatedAtutclong@Semantics.systemDateTime.createdAt: true创建时间
LastChangedBysyuname@Semantics.user.localInstanceLastChangedBy: true最后修改者
LocalLastChangedAtutclong@Semantics.systemDateTime.localInstanceLastChangedAt: true用于乐观并发控制的 ETag
LastChangedAtutclong@Semantics.systemDateTime.lastChangedAt: true草稿的总 ETag

输出格式

  • 创建新视图时,提供完整的 CDS 源代码
  • 包含所有相关注解
  • 遵循命名约定(接口使用 ZR_*,消费使用 ZC_*,复用使用 ZI_*)
  • 始终公开消费中使用的关联
  • 与安全相关时,包含访问控制定义

参考资料

CDS 的 Clean Core 级别说明

场景级别说明
从已发布的 SAP CDS 视图(I_*)中执行 SELECTA目标状态
基于自有表创建自定义 CDS 视图A允许在同一软件组件内使用
从经典(指定)SAP CDS 视图中执行 SELECTB在经典 ABAP 开发中可接受
基于未发布的 SAP CDS 视图创建包装 CDS 视图B/C如果包装的视图是经典 API,则为 B;否则为 C
直接对 SAP 数据库表进行读取访问CATC 优先级 2——使用已发布的 CDS 视图或经典 API
直接对 SAP 数据库表进行写入访问DATC 优先级 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 视图

评论

支持 Markdown