1. 为什么我劝你把 ABAP 练到“能上云”
先讲个挺现实的场景。去年我跟一个做了八年 ECC 开发的朋友聊天,他问我:现在甲方招人,开口就是 Clean Core、S/4HANA 云就绪,这套东西到底要怎么学?我反问他:你现在写一个报表,从 SE38 到输出 ALV,要动哪些东西?他说:建表、写 SELECT、拼 layout、塞 fieldcat,运气好一天能交。我说:这就是问题——还在“写程序”,而不是“搭服务”。
ABAP 这门语言被唱衰了不止十年,但真正在 SAP 生态里干活的人都知道,它不但没死,反而在 S/4HANA 时代换了一套更难、更值钱的玩法。传统 ECC 里的那些套路——直接改标准表、在标准程序里打增强、把业务逻辑写进报表——到了云环境,或者说到了 Clean Core 的架构要求下,全都不成立。你不能再往标准表里塞自定义字段,不能再靠隐式增强硬改标准行为,甚至不能再把 ABAP 当成“能在服务器上跑的 Excel 宏”来用。
那怎么办?不是不写 ABAP 了,而是要把 ABAP 写得更“现代”。现代不是指语法时髦,而是指你对这门语言的定位变了。你得把它当成一门能构建云服务的后端语言来练。ABAP 在云端不再是你一个人的玩具,它要暴露 OData 服务、要处理幂等、要遵循生命周期管理、要跟 Git 和 CI/CD 打交道。你能不能在不上手一套全新技能栈的前提下,把 ABAP 用成一种“云原生后端语言”?答案是能的,但路径跟大部分老 ABAPer 的直觉是反的。
这篇文章就是一条我自己走通的路线。它不是三个月速成,也不是让你从零学 Java 或 Python 然后抛弃 ABAP。它讲的是一套“最小技能栈”——你不需要先学完 SAP Cloud Platform、不需要一上来就啃 RAP 一百个类,而是先把 ABAP 这门语言本身练到能在云架构里生存的水准,再去碰 Clean Core 的边界与规范。换句话说:先学会用新规矩写老代码,再学会在标准系统里克制地写新代码。
适合谁看?两类人。一类是在 ECC 里写了三五年、想往 S/4HANA 云或 RISE 方向转的 ABAP 开发;另一类是刚开始学 ABAP、怕自己学完就过时的学生或转型者。我会把整条路线拆成四个层面:最小技能栈练什么、Clean Core 到底卡什么、从传统 ABAP 到云 ABAP 的实操迁移怎么做、以及那些网上没人告诉你的坑。
先给结论:ABAP 上云这事,难点从来不在语法,而在思维。你做 ABAP 做久了,最值钱的不是你会 SE80,而是你懂业务表关系、懂标准程序行为。这套功底在云时代没有贬值,但你需要换一批工具去承载它。下面我按自己的学习顺序拆开讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 最小技能栈:不是让你重学 ABAP,是让你“精炼” ABAP
2.1 先想清楚:什么样的 ABAP 技能到了云端还依然有效
很多人一听“上云”,以为要学一大堆新语言、新框架。实际上你把 SAP 云环境下 ABAP 的招聘要求翻出来,核心还是那几样东西:CDS 视图、ABAP SQL(特别是新的 Strict Mode)、OData 服务建模、类和方法的设计能力、以及调试和性能分析。这些其实都是 ABAP 传统能力的“精炼版”,而不是推倒重来。
什么叫精炼?举个例子。传统 ECC 里查数据,大家习惯了 OPEN SQL 一把梭,SELECT * 全表拿回来,然后内表套内表在 ABAP 里做逻辑。这套写法到 S/4HANA 还能跑,但 AWS 上跑、云上跑,性能问题会被放大到你无法忽视。云端 ABAP 环境对数据访问有严格限制:必须推到数据库层做过滤、必须减少数据传输量、必须在 SELECT 里显式指定字段。这不是什么新语言,这就是 ABAP 语言本身的用法规范,只是在传统 ECC 里你不遵守也没人管你,到了云上不行。
我自己的体会是,最小技能栈的第一课,不是去学 RAP 或 OData,而是把 ABAP SQL 重新学一遍。我说的重新学,是指你要能回答这些问题:SELECT 里能不能用 LEFT JOIN 后跟子查询?CDS 视图里 join 和 association 有什么区别?UNION 在 ABAP SQL 里的限制是什么?如果你对这些概念还很模糊,那云上的 ABAP 你会写得很痛苦,因为你连数据都取不顺。
再往下,就是 ABAP 语法层面的“去工业化”。云 ABAP 里允许的写法比传统 ECC 更严格,比如:废弃了很多隐式类型转换,严格要求变量声明清晰,禁止使用过时的语法。写惯了旧项目的人会一时间很烦。但换个角度想,这正是 ABAP 这门老语言焕发新生机的信号——它开始向现代工程化的方向约束自己。
2.2 最小技能栈的五个模块,不贪多,但必须扎实
我自己总结下来的最小技能栈,总共五个模块。不多,但每一个都有明确的“练到什么程度”的标准。
第一个模块:标准 ABAP 语法与内置函数。别笑,这个真的要重新过一遍。重点不是 do loop 那些,而是字符串处理、内表操作、数值类型判断这类日常小题。网络热词里有个“abap 检查是否为数值类型”,这背后其实是一类需求:从外部系统拿过来的字段可能是带空格的字符串,你要判断它能不能转成数字。很多人第一反应是写个正则,甚至有人直接调 cl_abap_matcher,其实 ABAP 自己就有 IS NUMERIC 的简单方式,配合 COND 可以做得很优雅。这种“知道内置能力,不重复造轮子”的判断力,就是模块一的核心。
第二个模块:ABAP SQL 与性能思维。这个我上面已经提了下,这里给个可量化的标准:你写一条 SQL,要能说清楚它会被翻译成什么数据库访问计划。SELECT 里 join 顺序、where 条件下推、CDS 视图的 where 和 having 区别,这些都要有感觉。另外一个特别的点:ABAP sort 和 ORDER BY 的区别。ABAP 里 sort 是内存内表排序,ORDER BY 是数据库端排序。搞清楚什么时候用哪个,能避免一半的“程序响应慢”问题。有人觉得 sort 比 ORDER BY 快,其实未必——数据量小的时候内存排序可能快,数据量大、还要加后续关联操作的时候,数据库端排序配合索引反而更优。这块需要用实际数据去测,不要凭感觉。
第三个模块:数据建模与 CDS 基础。CDS 不只是“新的视图”,它是 ABAP 云端编程的地基。最小技能栈阶段,不要求你会写全部注解,但至少要知道:CDS 视图的分层思想(基础视图、消费视图)、association 跟 join 的建模差异、以及如何通过 @OData.publish: true 把一个 CDS 直接发布成 OData 服务。这一块是打通 ABAP 和云端服务的关键桥梁。
第四个模块:会话与状态管理。别觉得这是 Java 才有的概念。ABAP 里的 lock object、FM 里的 COMMIT/ROLLBACK、RFC 调用的会话管理,其实都是状态管理的 ABAP 表达。云端 ABAP 和传统 ECC 有个本质区别:以前一个 report 跑完,数据在内存里想放多久放多久;现在一个请求进来、处理完、会话可能就被回收。所以你要重新理解“无状态”这件事怎么在 ABAP 里落地。这个模块,网络上有个热词叫 abap dequeue_all,其实就是锁释放的管理。不只是 DEQUEUE_ALL 这一个函数,而是你要理解在一个 OData 请求里什么时候加锁、什么时候必须释放锁,否则就是锁泄漏。
第五个模块:接口与数据交换。这个模块学得越早越好。云环境里,ABAP 不是一个孤岛,它要和前端、和外部系统交互。所以你要会 OData 的基础消费方式,会看 API 文档,能理解 JSON/XML 的结构。ABAP 里用 /ui2/cl_json 把内表转 JSON 输出,这几个类怎么用,必须练到条件反射。
这五个模块如果你都到了“能不看 F1 帮助直接写出来”的程度,那你的 ABAP 就算“能上云”了。注意,这阶段你还没碰 RAP,还没碰 SAP BTP,但这些基本功已经足够让你在云环境里快速成长。我一直觉得,很多人在 RAP 里写得磕磕绊绊,卡住他们的不是 RAP 的概念,而是基础不牢——比如一个 DB 读写操作写不利索,再套上情绪化的错误校验和状态管理,整个人就懵了。
2.3 套路学习法:用新规范重写你的“老代码库”
学最小技能栈最高效的方法,不是去背新语法,而是回头把你过去写过的老程序用新规范重写一遍。你要是有旧项目代码,挑三个出来:一个纯报表、一个批量处理程序、一个对话框程序。然后把它们分别改造成:基于 CDS 的数据消费版本、基于 AMDP 或 ABAP SQL 优化的版本、以及暴露为 OData 服务的版本。
这个过程你会踩到很多具体问题,比如:老程序里用的 Internal Table 排序,是应该保留 ABAP sort 还是改成 SQL 的 ORDER BY?如果你的数据量在几千行以内,ABAP sort 确实简单直观;但如果数据来源于多表 join,你最好在数据库层就把排序做了,这样配合新 CDS 视图,整体性能会好看很多。再比如:老程序里的 function module 能不能直接改成类方法?能,但别为了改而改,要看这个函数是否被多处调用,RFC 的兼容性要保留,这又涉及到后面要讲的 API 治理。
重写的过程,就是你把“旧经验”翻译成“新语言”的过程。很多老 ABAPer 觉得云时代自己一无是处,其实不是,他们缺的不是知识,是翻译能力。
3. Clean Core:不是技术规范,是克制与边界
3.1 Clean Core 到底是在说什么
Clean Core 这个词在 SAP 圈子里火了好几年了,但真正理解它的人不多。它不是一个具体的技术栈,也不是一个工具,而是一套对扩展方式的边界要求。核心就一句话:保持 SAP 标准系统干净、稳定、可升级。标准系统不干净了,你升不了级、出问题排查不了、被 SAP 支持和云运维模式卡得死死的。
很多人会有疑问:什么叫不干净?说白了就是你“碰了不该碰的东西”。在传统 ECC 时代,我们最习惯做的事就是改标准表、改标准程序。比如给标准表 APPEND 一个 Z 字段,这是最常见也最被滥用的自定义。再比如改 SAP 标准功能模块的源代码,往里面塞一段自己的逻辑。这些操作在 ECC 时代没人管你,在 S/4HANA Cloud 直接就不让你做——不是道德问题,是技术上限,标准对象被锁住,你无法修改。
我自己的理解,Clean Core 是 SAP 对过去几十年客户化定制问题的一个“纠偏”。以前你想让系统满足需求,最方便的就是改标准对象。改完以后,升级变成噩梦:SAP 发个新版本,你的修改直接冲突,只能重新 MERGE,MERGE 又可能带来新问题,然后 IT 部门就只能“冻结升级”。冻结升级的后果更可怕:你错过新功能、错过安全补丁、错过法律合规更新。所以 Clean Core 不只是一个技术规范,它是 SAP 商业模式的延续——保证每季度能安全升级,保证云模式能运转。
3.2 Clean Core 对 ABAP 开发的三条铁律
第一条:永远不要在标准对象上直接修改。这一条没什么可商量的。你想扩展一个标准程序的行为,正确姿势是使用 BAdI 或者显式增强点(Enhancement Point / Implicit Enhancement)。下下策是去复制一个标准程序为 Z 程序来修改——虽然这个做法在 ECC 时代很普遍,但在 Clean Core 评估里,这已经被视为一种污染,因为你散落了大量重复代码,还会让后续升级陷入混乱。
第二条:扩展数据模型时,优先用附加表(Extension Table)并保持与业务对象的关联,而不是往标准表上钉自定义字段。S/4HANA 提供了扩展字段(Extension Field)的机制,利用发布好的扩展点加字段,数据可以跟业务文档关联检索。如果你需要的关系更复杂,就得考虑自定义表(Z 表)和标准表建立外键或逻辑关联。这又回到了 CDS 视图建模能力的考查——你的建模水平,直接决定了扩展的优雅程度。
第三条:一切自定义开发都必须以可发布为 API 为前提。Clean Core 里有个很核心的概念叫“已发布对象”(Released Object)。它不是说你代码能跑就行,而是要符合 SAP 的 API 契约。SAP 保证在许可范围内不破坏已发布对象的兼容性,所以你的扩展代码如果只依赖已发布的对象,那升级的时候就不大会被冲垮。反过来,如果你碰了未发布对象,SAP 不给你任何兼容性承诺,升级崩了只能自己扛。这一条是很多老开发特别不适应的一点:以前我们是“能用就行”,现在是“你依赖的东西得是官方承诺不变的东西”。没有 API 契约的开发,在云上就是定时炸弹。
3.3 一个数据表扩展的真实案例:标准表加字段的三种正确姿势
咱们以一个具体场景来把 Clean Core 落地到代码层面:用户需要在物料主数据(MARA 表)上增加一个自定义信息,比如“本厂推荐供应商编码”。老办法是直接往 MARA 里 APPEND 一个 Z 字段,这招在 Clean Core 严格管控下是不推荐的,因为 MARA 是标准核心表,SAP 明确禁止直接修改。那怎么办?三条路线。
路线一:用 SAP 官方扩展机制。如果这个字段只是给主数据维护界面用的,你可以通过 EXTENSION FIELD 的方式挂在标准的 MDM 界面上,这些扩展字段存储在专门的扩展表中,SAP 在升级时自动兼容。在云环境里,这种方式最省事。
路线二:创建 Z 扩展表,用逻辑外键关联 MARA。比如建一个 ZMARA_EXT 表,主键是 MATNR(物料号),加一个字段 ZSUPPLIER(字符串类型)。你的 ABAP 代码里,在读取物料数据时做二次读取或者 CDS view 关联。这条路适合字段较多、需要自己做校验和逻辑的场景,也方便后续做接口发布。
路线三:如果自定义信息主要是在报表或接口里消费,不想占主数据主界面,那就走 CDS 视图扩展。你基于 I_Material 这类的标准 CDS 基础视图写一个自己的扩展视图,用 association 关联 Z 表数据,再用发布场景暴露给 Fiori 或 OData。
这三条路线的选择逻辑是什么?我的经验是:能走官方机制就不要自己造表;字段少就走扩展表关联,字段多且关系复杂,优先建立自己的领域对象模型。以上例子里,推荐路线始终是一、二优先,三适合分析场景。
4. 从 ABAP 到云 ABAP:一条能复刻的实操成长路径
4.1 里程碑拆分:先成为“能用 ABAP 写服务的人”,再成为“Cloud-Ready 的全栈”
很多朋友学东西没有里程碑,学一个月就放弃了。我自己给这条路线设计过一套可验证的里程碑,分享给大家参考。
第一个里程碑:能写出符合新语法规范的 ABAP 类和方法。什么叫符合规范?代码里没有过时的隐式转换、没有 MOVE 直接把字符往数字里塞、没有 OCCURS 0。你写一个类,方法参数显式、异常处理清晰、能用 EXPORTING / CHANGING 就少用全局变量。到这个阶段,你算是把 ABAP 的语言规范捡起来了。
第二个里程碑:能用 CDS 建模一个业务场景的数据模型。不需要炫技,就做一个简单的销售订单分析场景。新建两个 CDS 视图:一个基础视图从标准表取数据,另一个消费视图做聚合。然后把这个消费视图发布成 OData 服务。如果你能自己用云 API 调通这个 OData,就算又一次达成里程碑。
第三个里程碑:理解 OData 的 CRUD 生命周期,并在 OData 服务里把 ABAP 的 lock、transaction、validate 串起来。说得具体一点:你要能实现一个“创建物料扩展信息”的服务,POST 进来的 JSON 先校验,再设置排他锁,再检查是否已存在,最后 COMMIT WORK。这里每个环节都对应 ABAP 里一个具体能力。
第四个里程碑:把整个工程接到 CI/CD 里。这里会用到 gCTS 或者 abapGit + GitHub Actions 这类工具链。你看到的很多“上云”岗位需求,其实卡人的不是写代码,而是自动化流程。ABAP 开发也要学会把你自己的代码提交到云端仓库,让流量走 Pipelines 构建出可传输的软件包。
第五个里程碑:理解 Clean Core 边界,能判断一个需求属于“应扩展”还是“不应碰”。到这个阶段,你已经不是编码员,开始往架构师方向走了。
4.2 实操:从一个老式报表改造为 OData 服务,完整过程拆解
下面这个例子我实际带人做过不止一次,拿出来跟大家拆一遍。假设我们有一个传统的 ALV 报表,叫 ZRPT_MATERIAL_STOCK,根据物料号查询库存信息,输出到一个 ALV GRID。现在要把它改造成一个面向移动端 Fiori 应用或外部系统调用的 OData 服务,返回物料号、库存地点、库存数量等字段。
第一步:整理业务逻辑,识别查询条件。老报表里可能有 SELECTION-SCREEN 上这几个参数:物料号、工厂、库存地点。这些参数最终会成为 OData 查询选项($filter 或自定义 query option)。注意,报表里的 ORDER BY 逻辑要翻译成 CDS 里的排序或 SQL 里的字段顺序。
第二步:定义 CDS 视图作为数据源。如果系统里已经有标准 CDS 视图(比如 I_MaterialStock),优先站在上面扩展。如果标准视图不够,再自己写基础视图。视图字段不用贪多,精确映射报表输出那几列即可。定义完成后,可以在 ADT 里直接按 F8 预览查询结果,验证数据量、筛选逻辑是否一致,这一阶段相当于把老代码里的数据库访问逻辑“上移”到了 CDS。
第三步:创建 OData 服务。在云 ABAP 环境,最常规的方式是把 CDS 视图通过 @OData.publish: true 暴露,或者在 RAP 里基于 CDS 创建行为定义,生成 OData V4 或 V2 服务。这里有两条路径,简单查询用 CDS 直接暴露,需要考虑写操作、状态管理、校验逻辑时,走 RAP 更稳。在老 ECC 环境,你可以用 SEGW 构建一个 OData 项目,把 CDS 或表结构 import 进来,再映射方法。无论用哪种,本质上都是把 ABAP 里的数据交互能力包装成了标准接口。
第四步:实现查询逻辑。在 RAP 场景下,查询逻辑通常不用你写(默认行为),但你需要明确筛选条件。在 SEGW 场景下,你需要在 *SET_ENTITYSET_QUERY 里写 ABAP SQL,把请求参数映射到 WHERE 条件。这里的关键细节是:要对前端传来的 $filter 字段做白名单校验,别让外部入参直接拼接 SQL,防注入。我以前见过一个同事写 OData 查询时,直接把 iv_filter 字符串拼到 OPEN SQL 里,结果一个引号就能把服务搞挂,这种事要严防。
第五步:事务与锁。如果这个服务只是只读查询服务,那事务这一层比较简单。但如果你要扩展成“修改库存扩展字段”的服务,就得在行为类里用 MODIFY 操作语法,加 lock 管理。前面提到过的 DEQUEUE_ALL 这类锁释放函数,在云上已经有新的建模方式,但原理没有变:请求进来先 lock,处理完必须释放;如果出现异常,也要在 cleanup 里释放锁,否则会话残留锁,用户下一次请求就卡死。
第六步:测试。用外部 API 工具(如 Postman)调用 OData 服务,验证 CRUD 行为,检查 HTTP 状态码和报错数据,比在 SE80 里看结果画面更接近云上真实调用方式。
这一通操作下来,你应该会意识到:所谓 ABAP 上云,不是写不出来代码,而是要建立一套面向接口、面向生命周期、面向自动化测试的新工作习惯。
4.3 不要一上来就学 RAP:先按“能不能跑在无状态环境”来调整习惯
学习顺序特别重要。我见过不少朋友,听说 S/4HANA 云 ABAP 要用 RAP 开发,就兴冲冲去买一本 RAP 书,结果看了三章就劝退——因为 RAP 牵涉 CDS、BOPF、行为模型、OData,知识量太大了。新手没有胶水知识,去看 RAP,就是看天书。
我的建议是:先别碰 RAP,先用最小技能栈把“无状态环境下的 ABAP”跑通。什么是无状态?就是不允许你在类属性里存业务数据、不允许一个内表在两次方法调用之间传递数据、一切数据必须通过参数在方法与服务之间显式流转。你把一批旧代码按“无状态”的规矩重构一遍,就是在为 RAP 预热了。等你能轻松写出无状态的 ABAP 类之后,再看 RAP 的行为定义和 draft 概念,你会发现它的设计意图顺理成章:draft 就是“还没提交之前的临时状态”,但那也是需要显式管理的,不是放在全局变量里的。
很多人一上来就卡在这种全局变量习惯上。以前写 ABAP,喜欢在 TOP INCLUDE 里定义一堆全局内表,程序各处随便赋值。这个坏习惯在云端开发里是致命的,因为一个 HTTP 请求的执行上下文不是持久的,你的全局变量可能在下一次调用时就被回收了,数据就丢了。哪怕代码能跑,也会出现“第一请求正常,第二个请求莫名报错”的诡异问题。
我反复跟人讲一句话:ABAP 上云,练的不是新框架的配置,而是“你在哪里存数据”的判断力。数据应存在数据库、存在请求上下文、还是存在外部缓存,这比你会不会某个类更核心。
5. ABAP 上云常踩的坑:十一个问题排查与避坑指南
5.1 问题速查表
我在实操和带人的过程中,整理了一批高频问题。
| 现象 | 根因 | 解决思路 |
|---|---|---|
| OData 服务跑通,但 Fiori 端报 404 | CDS 视图发布后服务路径配置不正确 | 检查 OData 服务注册 URL,使用 /sap/opu/odata/sap/ 前缀核对 |
| 前端调用报 500,后台没日志 | 异常处理捕获不完整 | 在行为类或服务类里加全局异常 Handler,显式记录错误消息 |
| 同一请求连续提交两次,产生重复数据 | 缺少幂等校验 | 在创建逻辑前查重或使用唯一主键检测 |
| 数据无法按 CDS 视图的排序输出 | ORDER BY 写在了视图外部消费层 | CDS 本身的排序能力有限,把排序放到消费层(OData 的 $orderby)或抽象层处理 |
| SELECT 字段不够导致 ALV 数据缺失 | 新视图字段映射不全 | 对照原报表字段清单逐个核对,特别是屏幕输出前有计算字段的情况 |
| 报表改造后,性能反而不如老报表 | CDS 视图 join 太复杂,未使用索引 | 分析执行计划,避免在 CDS 中使用过多 union,适当将视图拆分 |
| 用户反馈修改后数据没变 | 忘了 COMMIT WORK | 云 ABAP 环境下,事务控制要显式声明;未 COMMIT 的改动在后续读取里可能不可见 |
| 外部接口收到数据类型错误 | JSON 里字符串和数字类型转换没处理好 | ABAP 转 JSON 时做好显式类型映射,字符字段用 numc 或字符串转换类辅助 |
| 程序在 ECC 正常,迁移 S/4HANA 后 SELECT 失败 | 标准表结构有兼容性问题 | 用 S/4HANA 兼容性分析工具扫描代码,定位废弃字段并替换 |
| 自定义表数据量庞大但读取很慢 | 没有建立合适的二级索引 | 分析查询条件,补建索引;注意 S/4HANA 里 ABAP 层索引的影响,避免索引盖在低选择率字段上 |
| 锁残留导致后续请求全部超时 | 异常分支未释放锁 | 在 finally 或 cleanup 块统一调用 unlock,不要只在正常路径释放 |
5.2 排查步骤的核心逻辑
排查这类问题的套路其实很一致。
第一步,把异常从“现象”降级到“具体请求”。别一上来就翻代码。先用 Postman 或 SAP Gateway Client(SEGW 里带的那个 OData test 工具)直接调服务,把 URL、Header、Body 保存下来,把你的“业务问题”变成一个“HTTP 请求问题”。这一步能过滤掉一半的前后端联调噪音。比如出现 500,你点进去看响应体里有没有 SAP 的错误消息结构,很多问题一眼就能定位。
第二步,看 ABAP 运行时的短文本和日志。ST22 能看 dump,SLG1 能看应用日志。不要嫌麻烦,云环境里很多错误不会弹出友好提示,它只会很安静地写到 log 里。我见过有人排查了一天,最后是因为一个数据校验逻辑抛了 CX_SY_ZERODIVIDE,日志里写得清清楚楚,只是没人去看。
第三步,做最小化复现。把请求里的参数一个一个去掉,直到问题不再复现,那个参数基本就是元凶。这个方法听着笨,但比对着文档猜要快得多。
5.3 关于“abap 一年前”这类日期比较的细节技巧
热搜词里有个“abap 一年前”,其实就是日期比较和日期计算的需求:筛选出一年前的数据。写的时候要注意几个坑。其一,直接用 SY-DATUM 减 365 天会遇到闰年问题,跨闰年时少了一天。稳妥做法是用 CL_ABAP_CONTEXT_INFO=>GET_SYSTEM_TIME(或者直接 CONVERT DATE)配合 CL_ABAP_TIME_UTIL 做日期运算,或者用 ADD - 1 YEARS TO 的字句方式。比如:
abap复制DATA(lv_one_year_ago) = uv_date.
ADD -1 YEARS TO lv_one_year_ago.
这样写出来,语义清晰,也不会被闰年坑。其二,日期比较时注意字段类型,如果源数据是 CHAR8(YYYYMMDD)和 DATS 混用,要先统一再比较。其三,如果筛选发生在 CDS 视图里,可以直接用 $ 系统字段,比如 _ 和 $session.user_date 一起做条件判断,让查询下推到数据库,性能更好。不过要注意,CDS 里做日期运算要用 SQL 标准函数,判断逻辑不同,所以在 CDS 视图和 ABAP 程序里不要随意混用日期类型的写法。
6. 工具链与工程化:ABAP 上云真正拉开差距的地方
6.1 从 SE80 到 ADT:换一副更“云化”的开发眼镜
聊到这里,有个东西绕不开:开发工具。如果你还在 SE80 里写 ABAP,那我可以直说,你已经不适合谈“上云”了。不是说 SE80 不能写,而是它对现代工程化流程的支持太弱:没有本地文件版本管理、没有静态代码检查插件、没有自动补全到那种程度、对 CDS 的支持也远不如 ADT。ABAP Development Tools(ADT)之于 ABAP 开发,就像 VS Code 之于前端开发——不只是换个编辑器,是整个工作流都换了。
ADT 基于 Eclipse 生态,这个选择让 ABAP 开发可以用上一堆通用插件:Git 集成(通过 abapGit)、代码检查工具、甚至 SonarQube。你写完一个类,不再需要激活之后才发现语法错误;写的时候实时报错,改完直接做单元测试。云环境里还支持你直接在 ADT 里查看 CDS 视图的预览数据。这套体验,体验过之后再回去 SE80,你会忍不住叹气。
具体一点,我现在写 ABAP 的日常是:在 ADT 里建一个项目连接,从 git 仓库拉取代码,写完用 ABAP 静态检查跑一遍 SCLINT,再把代码提交回仓库。部署是通过 gCTS 自动传到测试系统,然后在测试系统做集成验证。这套流程,跟现代后端开发的体验几乎是一模一样的。以前那种“我在 DEV 系统写代码,然后手动到测试系统 trasport request”的老流程,在云端就变成“CI 构建之后自动触发测试环境部署”。
6.2 abapGit 与传输工具:代码版本的“二向箔”
经常有人问我:云上的代码怎么管理版本?答案是 abapGit。abapGit 实现了 ABAP 对象和 Git 仓库之间的双向同步。你在云端开发系统里写代码,可以一键导出到本地,形成一个文件结构;再推送到 GitHub/GitLab 之类的地方去。反过来,你在代码仓库里改文件,也可以拉回 ABAP 系统执行激活。这就让 ABAP 开发第一次有机会用上 code review、分支策略、SemVer 这种现代软件工程的日常手段。
我自己的建议是:单独建一个 Z 命名空间给自定义对象,用 abapGit 做成一个独立的 repository。不要把所有对象一股脑放到一个仓库里,那样 commit 历史一团乱。把对象按领域拆分:比如库存扩展一个 repo、销售增强一个 repo。每次改动尽量是一个独立 commit,描述写清楚改了什么,这样出现问题才能回滚。
老 SAP 环境里的传输请求(Transport Request)也是有必要的,但它的组织颗粒度比较粗,很容易出现“一个 request 里塞了一堆无关对象”的混乱。上云之后,我更推荐用“gCTS + Git”这套组合拳。gCTS 是 SAP 在云环境提供的代码传输机制,它基于 Git 管理代码版本,让系统间代码流转可回溯。这套组合,是我对“ABAP 上云工程化”最推荐的落地方式。
6.3 测试驱动在 ABAP 里怎么落地
很多老 ABAPer 听到“测试驱动开发”觉得这是别人家的文化,自己写代码能跑就谢天谢地了。可到了云环境,自动化测试越来越成为一个刚需——因为部署频率高了,手动回归根本忙不过来。
ABAP 里做自动化测试,核心工具是 ABAP Unit(AUNIT)。它能基于类和方法写断言,执行快速。早期 ABAP 的类确实难测,因为一堆依赖塞在函数模块里,到处是 DB 访问。解决依赖问题的思路是依赖注入:把数据源、用户上下文、外部调用的类都抽象成接口,测试时注入 mock 对象,这样可以很轻易地造极端数据。
举个例子,你在一个方法里要查库存、判断是否低于安全库存,然后发告警。传统写法是方法内部直接 SELECT,很难测。改造后:方法接收一个“库存查询器”接口,生产代码注入真实查询器,测试代码注入返回固定库存的 mock 查询器。这样你能测试“库存=0 时告警逻辑是否正确”而不用真的去数据库造数。ABAP 里实现 mock 很简单,新建一个本地测试类继承接口,返回固定值就行。AUNIT 的 cl_abap_unit_assert 提供了一系列断言类方法,写起来很顺手。
我个人的体会是,一旦你开始写可测试的代码,你的代码结构会不由自主地变好——因为你必须拆分依赖、抽离状态、显式传递参数。这些恰好就是云 ABAP 最需要的代码形态。学可测试代码,比专门去学设计模式更管用。
7. 从 ABAP 语言本身说开去:为什么这门老语言反而更贴近业务
我在文章开头说过,别把 ABAP 当“能在服务器上跑的 Excel 宏”。但反过来说,ABAP 之所以在云时代还能活得好,恰恰因为它跟业务的绑定比任何通用后端语言都深。你在 Java 里写一个订单接口,要自己设计表结构、自己管理事务、自己做权限校验;在 ABAP 里,订单、物料、客户这些业务对象天然存在,标准表结构、标准授权对象、标准事务逻辑,全都有。你要做的不是从零建,而是在已有的、稳定的业务底座上做扩展。
这个底座,就是 Clean Core 要保护的东西。Clean Core 不是让你不写代码,恰恰是为了让你写的代码能一直跑下去。标准系统不断升级,但你的扩展代码只要坚守已发布 API 的边界,就不会被升级碾压。所以,Clean Core 其实是 ABAP 开发者价值沉淀的保障:你的代码不会因为一次升级变成一堆需要重构的僵尸文件。
这句话值得再强调一遍:Clean Core 实现的不是封闭,而是可维护的自由。它让“云上 ABAP 开发”变得像在稳定的地基上盖房子,而不是在流动的沙丘上插篱笆。理解了这一点,你再看 SAP 的 One Domain Model、BTP、RAP,就不会觉得这是厂商锁定的算计,而是一整套“如何在持续演进的企业系统里长期生存”的工程哲学。
我自己做了这么多年 ABAP,最后的感受是:这门语言的未来不在语法,在生态。SAP 系统本身是企业数据的老窝,ABAP 就是这个老窝里最顺手的管家。上云之后,管家换了制服、换了流程,但管家的手艺还是那个手艺。你要做的是抬着头看新世界的规则,手里继续打磨你的手艺。
有朋友总问我:那我现在转 Java 还是转 Fiori?我的回答是:先把手里的 ABAP 按新规范练扎实。语言只是工具,真正值钱的是你能在一个复杂的企业系统里精准定位问题、设计稳定扩展的能力。你能把这个能力翻译到云端 ABAP,那它也能跟着你去任何技术栈。
