ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

S/4HANA CDS View到View Entity迁移实战指南

S/4HANA CDS View到View Entity迁移实战指南 前几个月做 S/4HANA 2022 升级前的准备时我被一堆 CDS view 的激活警告拦住了。警告内容并不复杂但指向性很强新代码应当使用DEFINE VIEW ENTITY。这个警告不是个例凡是涉及 RAP 模型、OData V4 服务、ABAP Cloud 开发的对象SAP 都在引导你用新语法。我当时的系统里还躺着几十个DEFINE VIEW直接改又不敢于是自己梳理了一套从旧视图迁到 CDS view entity 的实战流程。这篇文章就把这套流程写出来从迁移动机、依赖盘点、语法差异到编译报错处理和下游对象切换一次讲透。1. 从 DEFINE VIEW 到 DEFINE VIEW ENTITY这次迁移到底动了什么1.1 后缀从 VIEW 变成 VIEW ENTITY多出来的 ENTITY 不是一个摆设提到 CDSCore Data ServicesS/4HANA 项目里大家最早接触的肯定是DEFINE VIEW。这个语法从 S/4HANA 1511 时代就开始普及几乎所有自定义报表、Fiori 处理服务、APF 分析背后都有类似的一堆 DDL 源AbapCatalog.sqlViewName: ZDBV_DEMO01 AbapCatalog.compiler.compareFilter: true AccessControl.authorizationCheck: #CHECK EndUserText.label: Demo define view ZDEMO_VIEW as select from ekko { ebeln, bukrs, lifnr }普通开发者也早就习惯了写一段 DDL激活后 SE11 里自动多一个 SQL 视图ABAP 程序里直接SELECT * FROM zdbv_demo01就能查。DEFINE VIEW ENTITY则是在这之后定义的一种更贴近 SQL 标准、更严格的视图模型。它包含了 CDS view 的大部分能力但核心区别在于它不再以“ABAP 字典 SQL 视图”作为可查询对象。你在 SE11 里找不到它对应的 SQL 视图ABAP 程序要读取它是直接通过 Open SQL 访问这个实体本身。这个变化看着只是多了一个ENTITY后缀实际影响的却是激活机制、授权检查、下游对象兼容性以及整个对象生命周期的管理方式。1.2 推动迁移的几个源头RAP、OData V4 和 ABAP Cloud推荐用 view entity不是因为 SAP 想换一套玩法而是有明确的业务驱动RAPABAP RESTful Application Programming Model的 BO 根实体必须使用 CDS view entity传统DEFINE VIEW无法直接挂到行为定义上。如果你要做 S/4 的新增扩展、副作用增强、草稿功能基本绕不开这个前置条件。OData V4 服务和对应的 Fiori Elements 新模板SAP 在版本演进中逐步把 view entity 作为一等公民。同一个视图如果做成 entity元数据派生、访问控制、导航属性处理都会更平滑。ABAP Cloud 开发模型中云环境里能用的是DEFINE VIEW ENTITY老式DEFINE VIEW在 ABAP Cloud 环境下会被直接排除。哪怕你现在还在做经典的 OP 版本项目为了将来上云或做 BTP 扩展也得提前统一建模语言。我的体会是与其说这是一次“语法迁移”不如说这是一次“运行时制品迁移”。以前好多工具、监控报表、权限 DCL 都依赖字典里的 SQL 视图对象而现在 SAP 希望你把这些依赖收口到 CDS entity 本身少一层中间物多一分语义约束。1.3 少了字典 SQL 视图这件事对存量系统影响不小DEFINE VIEW激活时AbapCatalog.sqlViewName指定的 SQL 视图会成为 ABAP 字典里的正式对象可以被 Open SQL、RFC、BW 抽取、第三方 ETL 使用。而DEFINE VIEW ENTITY激活时不会生成这个字典对象。这个差异在绿色字段项目里没什么感觉但在存量 S/4 系统里影响很大。最常见的就是程序里写了SELECT * FROM zdbv_demo01。迁移后如果直接把 DDL 改成 entity这个字典对象就消失了程序要么激活期报错、要么运行期直接 “Table not found”。所以在迁移规划阶段我通常第一件事是全系统搜一遍AbapCatalog.sqlViewName对应的名字看有多少 ABAP 代码和外部接口在消费它。搜完你才会知道这次迁移不是“改几行 DDL”那么轻巧。另外授权控制也有差异。传统 view 默认情况下若没有 DCL 角色相当于完全开放而 view entity 在激活时对AccessControl.authorizationCheck的要求更明确如果没有对应的 DCL 对象有些版本会直接给警告或激活失败。这个不是小事迁移时经常有人被“还没有 DCL 角色”卡住。提示迁移前先确认目标系统版本。S/4HANA 2020ABAP 7.55以后 Open SQL 才能方便地把 CDS view entity 直接作为查询源旧版本里很多情况下你还是得包一层传统 view。2. 一个采购订单视图的整段迁移旧语法与新语法的差异清单2.1 原始 DEFINE VIEW 实例我拿一个简单又典型的采购订单视图来演示。原视图有两个关联表EKKO采购单据头和 EKPO采购单据行外加一个数据库字段的类型转换AbapCatalog.sqlViewName: ZDBV_DEMO02 AbapCatalog.compiler.compareFilter: true AccessControl.authorizationCheck: #CHECK EndUserText.label: 采购订单视图 define view ZDEMO_PO_VIEW as select from ekko as a inner join ekpo as b on b.ebeln a.ebeln { a.ebeln as PurchaseOrder, a.bukrs as CompanyCode, a.lifnr as Supplier, b.ebelp as PurchaseOrderLine, b.matnr as Material, cast(b.netwr as abap.fltp) as NetValueFltp } where a.lifnr 现实项目里可能比这个复杂十倍但这个例子已经能说明问题有 JOIN、有计算字段、有 CAST、有 WHERE 过滤。2.2 改成 DEFINE VIEW ENTITY 后的代码整体结构几乎原封不动需要调整的是注解和下层的运行时绑定AccessControl.authorizationCheck: #CHECK EndUserText.label: 采购订单视图 define view entity ZDEMO_PO_ENTITY as select from ekko as a inner join ekpo as b on b.ebeln a.ebeln { a.ebeln as PurchaseOrder, a.bukrs as CompanyCode, a.lifnr as Supplier, b.ebelp as PurchaseOrderLine, b.matnr as Material, cast(b.netwr as abap.fltp) as NetValueFltp } where a.lifnr 看代码两者几乎一样。真正变化的是两处去掉了AbapCatalog.sqlViewName。因为这个注解的用途就是生成字典 SQL 视图view entity 没有这一层。去掉了AbapCatalog.compiler.compareFilter: true。这个属性本来用于传统 view 的编译器行为控制迁移到 entity 后没有实际意义留着部分版本会直接报警告或激活失败。2.3 新旧语法差异速查表对比点DEFINE VIEWDEFINE VIEW ENTITYDDL 关键字define viewdefine view entity字典 SQL 视图生成必须指定 sqlViewName不生成无需也禁止 sqlViewNameOpen SQL 直接访问通过 SQL 视图名直接用 entity 名RAP 行为定义根实体不支持支持OData V4 推荐数据源一般不用推荐ABAP Cloud 可用性不支持支持类型一致性校验宽松字典层常自动兜底严格必须显式 CASTJOIN 键字段精度要求部分场景容忍类型差异严格两侧类型必须一致参数化视图支持支持参数注解更规范Association 目标可指向 view / entity更倾向 entity稳定后才可关联2.4 老开发容易忽略的三个差异第一字段可见性变化。在传统 view 里sqlViewName 生成的 SQL 视图字段名来自 CDS 别名在 view entity 里Open SQL 直接面对 entity 元素名。写 ABAP 程序时要注意大小写和数据库字段名的差异PurchaseOrder在程序里一般会被自动转成purchaseorder之类的可解析名但如果你的程序长期依赖字典视图的物理列名改动面会比想象中大。第二计算字段的类型宽度。传统 view 中substring、concat这类表达式在生成字典视图时往往会被系统按 SQL 规则推导出较宽类型view entity 里如果你不做显式 CAST激活阶段可能直接报“无法确定字段类型”或“类型长度不一致”。所以我给团队定了一个规矩从旧 view 迁到 entity 时凡是表达式、CASE、字符串拼接产生的字段一律显式写 CAST。第三WHERE 条件的尾随空格语义。ABAP 字典视图在比较 CHAR 字段时常常会做定长空格补齐而 view entity 更接近底层数据库的 SQL 行为。WHERE a.lifnr 这类条件在传统视图里能过滤掉空供应商在新实体上如果遇到 CHAR 字段建议写成WHERE a.lifnr 或使用 trim 类函数处理避免老数据“莫名其妙被过滤掉”。这是我们迁移后做全量比对时真实碰到过的场景。3. 先别急着改名依赖盘点与分批迁移计划3.1 怎么系统地查“谁在用这个视图”改代码之前我先用 ADT 的 “Where-Used List” 把目标视图的所有引用找了一遍。但仅仅在 ADT 里看不够因为消费方可能藏在 ABAP 程序、RAP 行为定义、OData 服务、SAP BW 抽取或第三方接口这些地方。我的做法是在 ADT 里右键 DDLS 源选择 Where-Used List先看 CDS 内部依赖。再用 SE38/SEC81 等方式全库搜索 sqlViewName 对应的名字如zdbv_demo02确认有没有程序在用SELECT * FROM zdbv_demo02。检查增强与扩展对象搜EXTEND VIEW、EXTEND VIEW ENTITY、Metadata.allowExtensions等注解注意业务增强字段是不是挂在原视图上。检查是否作为 OData 服务或 RAP 模型数据源查看 Service Definition、Service Binding、Behavior Definition 的引用情况。如果系统里跑了 BW/4HANA 抽取还要查一下 BW 转换/DTP 是否有该 SQL 视图作为数据源。做完这五步依赖的风险点基本就浮出水面了。3.2 把视图分成四类再决定迁移顺序根据调查结果我把视图分成四类分类特征处理建议无依赖没有下游对象引用直接改 entity风险最低仅供 ABAP 程序使用只有程序 SELECT 它先改程序再迁视图或同时传输被其他 CDS 视图引用存在多层依赖按底层到上层顺序迁移被外部接口 / RAP / OData 消费有 API 边界设计兼容层逐步切换我比较推荐“自底向上”的迁移顺序先把最底层的被依赖视图迁到 entity让上层视图基于新 entity 重建再一层层往上迁。这样做的好处是每次激活都尽量避开了“旧视图引用新实体、新实体还没激活”的循环依赖问题。3.3 分批计划的实战建议一个系统里若同时有几十上百个 CDS 视图不要妄图一次全迁。我的经验是第一批挑 1~2 个没有下游依赖的开发测试视图跑通整个“代码修改、激活、回归”流程看清楚你们的版本环境下哪些注解会有警告、哪些语法必须调整。第二批选一个 RAP 要用的视图把整个 RAP 模型的切换链路走通验证行为定义、OData 绑定、DCL 授权是否都正常。第三批再按业务域批量推进。每一批之间至少间隔一个完整的测试回归周期。迁移批次之间要保证传输顺序合理。DDLS、DCL、ABAP 程序、RAP 模型、OData 的激活有先后依赖一个批次里如果既有新 entity 又有老 view建议用一个传输请求统一收集并在目标系统按顺序激活。4. 改造过程中最常见的五个激活错误与解决思路4.1 错误一AbapCatalog.sqlViewName直接报错这个最常见。从旧代码复制过来的时候AbapCatalog.sqlViewName往往还在而 view entity 不允许这个注解激活时会直接报 “annotation not allowed”。解决很简单删掉。同时注意AbapCatalog.compiler.compareFilter、AbapCatalog.bufferType等一批和字典视图绑定比较紧的属性也可能需要同步清理。我的建议是把原视图上所有AbapCatalog.*开头的注解逐条看一遍不能确定意义的先不带过去。4.2 错误二CASE 表达式分支返回类型不一致旧 CDS view 里我写过很多类似这种情况case when a.bsart NB then b.matnr else a.ebeln end as ReferenceFieldb.matnr是 40 位的 CHARa.ebeln是 10 位 CHAR。传统 view 在激活时字典层经常睁一只眼闭一只眼按更大的长度或隐式规则生成 SQL 视图。view entity 对 CASE 分支的类型一致性要求严格得多编译阶段就会报错或警告。处理方式是给字段做显式 CASTcase when a.bsart NB then cast(b.matnr as abap.char(40)) else cast(a.ebeln as abap.char(40)) end as ReferenceField也可能是数值型分支不一致比如一个分支是 INT4、一个分支是 DEC需要统一成更高精度的类型。4.3 错误三JOIN ON 条件两边字段类型不匹配底层自定义表如果 join 标准表经常出现两边关联键长度不一致的情况比如标准 EKKO-EBELN 是 CHAR 10自定义表ZZZZPO~EBELN是 CHAR 12。传统 view 在部分数据库上能硬跑view entity 激活时会直接报关联键类型不一致。解决办法也是 CASTinner join zzzzpo as c on cast(c.ebeln as abap.char(10)) a.ebeln这里有个容易踩的细节CAST 之后可能影响索引使用导致查询性能下降。如果业务上能保证前 10 位就是完整单据号可以直接用cast(c.ebeln as abap.char(10))如果没法保证建议先在底层表上调整字段长度或换成长度匹配的视图。4.4 错误四Association 目标还是旧 DEFINE VIEW当一个 view entity 的 association 指向另一个尚未迁移的旧视图时如果系统版本对 entity 的 association 目标很苛刻激活会失败。实际中一般有两种处理把被关联的旧 view 也先迁成 view entity让 association 指向 entity。如果旧 view 已迁移但上层 entity 的 association 还写着旧视图名也会因为找不到对象而失败。所以迁移的时候最好不要只迁一个视图而是把存在 association 依赖的那一串视图一起排进同一个批次并保证先激活被依赖的底层对象。4.5 错误五表达式字段缺少显式 CAST导致下游宽度/类型不符字符串拼接concat(a.ebeln, b.ebelp)这类表达式view entity 对结果类型的推导往往比旧 view 更保守。如果这个字段要交给下游视图或 RAP 模型使用很容易出现 “field type too long” 或 “mismatch in field type” 之类的错误。在迁移时直接用 CAST 包一层能避免绝大多数问题concat(a.ebeln, cast(b.ebelp as abap.char(6))) as DocumentLine这个例子的含义是把行项目号先转成 CHAR 6再做拼接结果宽度就是 16 位符合业务预期。如果直接拼两个未指定宽度的字段编译器推断出的长度往往会比预期更大下游兼容就会出问题。排错的过程也不复杂优先从底层源对象开始激活每激活一个就刷新上层对象看错误列表。遇到实体视图报错时打开 ADT 的 Problem 视图按行号定位到具体字段或表达式通常一目了然。5. 切换下游消费方ABAP 程序、RAP 模型、OData 服务的平滑过渡5.1 兼容层要不要保留一个指向新实体的旧 DEFINE VIEW如果还有 ABAP 程序或第三方工具在使用旧的 SQL 视图名而你又不想立刻改完所有程序可以用一个传统 CDS view 来做兼容过渡AbapCatalog.sqlViewName: ZDBV_DEMO02 EndUserText.label: 兼容视图 define view ZDEMO_PO_VIEW as select from ZDEMO_PO_ENTITY { PurchaseOrder, CompanyCode, Supplier, PurchaseOrderLine, Material, NetValueFltp }这样原来的SELECT * FROM zdbv_demo02还能继续跑底层实际从新的 view entity 读取。注意这个方案不是免费的多包一层 CDS view生成的 SQL 会更复杂部分复杂 JOIN 下执行计划可能不如直接查 entity。所以我倾向把它当成过渡手段而不是长期双轨运行。过渡期结束、程序消费方都切到新实体后这个兼容视图就应该删除。5.2 RAP 模型切换行为定义和投影角色要一起动RAP 开发里Behavior Definition 的根实体原本指向传统 view 时建都建不起来。迁移做法是先建好 view entity并在上面补齐 RAP 需要的ObjectModel.*注解、权限注解、字段语义注解。修改 Behavior Definition把根实体换成新 entity 名相关 Behavior Projection 也要同步。检查 OData service definition 是否基于新 entity如果旧 service definition 还引用旧 view可以新建一个一律基于新实体的服务再切换 Service Binding。激活顺序上先实体、再行为定义、再投影、再服务绑定最后做端到端测试。常见问题新实体上面如果没有ObjectModel.usageType或ObjectModel.modelCategory这些注解RAP 模型经常在生成时告诉你“不支持该实体作为 BO”。别急对照旧视图的注解清单逐项补齐即可。5.3 OData 服务和 UI 注解要注意完整迁移很多团队做迁移时只关心 SQL 层能不能跑通忘了 UI 和语义注解。如果原视图上带了一堆UI.lineItem、UI.selectionField、Consumption.filter迁移成 entity 后如果不复制过去Fiori Elements 页面的字段显示和过滤条件会直接变化。我自己的做法是把传统 view 的代码复制到 entity 时除了AbapCatalog.*相关注解之外其余注解全部保留再按 ADT 的 activation 提示逐个清理无效项。这样能最大程度保持前端元数据一致不至于迁完之后页面字段少一半。5.4 什么时机切下游切换下游要选在测试周期的窗口里不要和生产激活混在一起。我的建议是开发环境先完成代码调整与单元验证。测试环境做全量回归用真实业务数据对比新旧结果。发布窗口内按“程序 - RAP - OData”顺序切换每切一个模块就做一次冒烟测试。最后删兼容层之前再看一遍 Where-Used确认真的没人用了再删除。6. 迁移后的回归验证与老视图退场流程6.1 数据一致性并行查询是唯一的硬标准代码迁移完并不代表迁移完成。在回归阶段我习惯写一段临时 ABAP 程序或直接在 HANA Studio 里做新旧两个视图的对账分别统计总记录数应当完全一致。挑 5~10 个业务字段做分组计数和 Top N 对比。对字符字段注意尾随空格差异只要一边用了 trim、一边没用结果就会不一致。对金额字段对比合计和平均值确认没有出现 NULL 转零或类型转换造成的精度损失。如果发现数量不一致不要急着往下走。先看是不是 WHERE 条件的空格比较、CASE 类型转换、JOIN 条件 CAST 造成的通常问题都出在这几个点。6.2 性能观察看执行计划不看表面 DDL传统 view 和 view entity 的性能差别并没有网上说的那么绝对。某些场景下view entity 少了字典视图这一层ST05 里的 SQL 会显得更“短”但也有场景是 HANA 优化器在传统 view 上早就做了视图合并性能本来就很好。正确的做法是迁移前后都在 ST05 里跑同一个查询尽量用相同过滤条件、相同绑定参数把 SQL 语句和执行计划捞出来对比。重点是看有没有出现意外的全表扫描、JOIN 顺序变化、CAST 导致索引失效。如果实体视图性能有所回退优先检查 JOIN 键两边的类型是否一致以及 WHERE 条件是否因为显式 CAST 而放弃了索引。6.3 老视图退场的三个前提要彻底删除旧 DEFINE VIEW我至少要确认三件事Where-Used 列表为空且全库搜索 sqlViewName 无任何 ABAP 程序引用。RAP、OData、BW 抽取等消费方已经全部切换到新实体。系统已经跑过至少一个完整测试回归周期没有出现数据库对象缺失的运行时错误。确认之后可以连同对应的兼容视图和旧 DDL 源一起传输删除。删除时要小心那些对象跨系统的状态如果其他系统还在引用传输删除后会引发激活错误。6.4 最后说点个人实操体会我做完第一个采购订单视图迁移时以为半天就能搞定结果在尾部空格比较和 JOIN 键类型上各耗了一天。第二次迁移就聪明了先把依赖盘点表做出来再把所有表达式字段的 CAST 提前写好整体速度提升了一倍。这套流程我后面在十几个视图上复用基本稳定。如果你手头也有批量 CDS 视图要迁不妨从最小的那个开始练手跑通之后再规模化比直接挑战核心复杂的视图要稳妥得多。
返回列表