2016年时,Jerry曾经写过一系列关于SAP Fiori Smart Template(如今改名为Fiori Elements了)的博客,介绍了所谓的MDD开发方法论 - Metadata Driven Development,即经过开发维护了对应annotation(注解)的CDS view,结合SAP WebIDE,可以花费最少的编程代价,就可以在短期内得到一个支持增删改查的Fiori应用。web
这个系列的博客集能够在Jerry这篇公众号文章里得到:Jerry的经过CDS view + Smart Template 开发Fiori应用的blog合集。sql
三年的时间过去了,ABAP在不断向前进化,现在咱们有了新的编程模型:Restful ABAP Programming模型,简称为RAP模型。该模型定义了一套架构体系,应用开发人员可以凭借其来高效地进行应用的端到端开发,这种应用具备与生俱来的Restful特质,能充分利用HANA平台的强大计算能力,支持云环境和Fiori UX。数据库
RAP模型的三大支柱:编程
下面请跟着Jerry一块儿,经过一个实际的例子,了解一下这种全新的经过Restful ABAP Programming模型进行Fiori应用开发的步骤吧。浏览器
Jerry仍是沿用传统ABAP On-Premises编程培训教材里使用过的经典的SFLIGHT模型来做为底层数据库存储。架构
(1)首先建立一个数据库表ZTRAVEL_JERRY:(若是想复制这段源代码,请点击文末的“阅读原文”得到)框架
@EndUserText.label : 'Database table for travel data XXX' @AbapCatalog.enhancementCategory : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #LIMITED define table ztravel_jerry { key client : abap.clnt not null; key travel_id : /dmo/travel_id not null; agency_id : /dmo/agency_id; customer_id : /dmo/customer_id; begin_date : /dmo/begin_date; end_date : /dmo/end_date; @Semantics.amount.currencyCode : 'ztravel_jerry.currency_code' booking_fee : /dmo/booking_fee; @Semantics.amount.currencyCode : 'ztravel_jerry.currency_code' total_price : /dmo/total_price; currency_code : /dmo/currency_code; description : /dmo/description; created_by : syuname; created_at : timestampl; last_changed_by : syuname; last_changed_at : timestampl; }
由于咱们在ABAP Development Tools里没法用事务码SE16手动往这张表里插入数据,因此我建立一个ABAP类,用ABAP代码往这个表里插入三条数据。ide
按F9执行这个ABAP类,而后看到三条数据成功插入了:spa
(2) 咱们最终的目的是建立一个支持对这张表进行增删改查的Fiori应用,而Restful ABAP Programming模型的三大支柱之一为Core Data Service,所以咱们首先得有基于数据库表ZTRAVEL_JERRY的CDS view.3d
因此我首先建立一个CDS view:
@AbapCatalog.sqlViewName: 'ZVI_TRAVEL' @AbapCatalog.compiler.compareFilter: true @AbapCatalog.preserveKey: true @AccessControl.authorizationCheck: #CHECK @EndUserText.label: 'Travel data - XXX' define root view ZI_TRAVEL_JERRY as select from ztravel_jerry as Travel /* Associations */ association [0..1] to /DMO/I_Agency as _Agency on $projection.agency_id = _Agency.AgencyID association [0..1] to /DMO/I_Customer as _Customer on $projection.customer_id = _Customer.CustomerID association [0..1] to I_Currency as _Currency on $projection.currency_code = _Currency.Currency { key travel_id, agency_id, customer_id, begin_date, end_date, @Semantics.amount.currencyCode: 'currency_code' booking_fee, @Semantics.amount.currencyCode: 'currency_code' total_price, @Semantics.currencyCode: true currency_code, description, /*-- Admin data --*/ @Semantics.user.createdBy: true created_by, @Semantics.systemDateTime.createdAt: true created_at, @Semantics.user.lastChangedBy: true last_changed_by, @Semantics.systemDateTime.lastChangedAt: true last_changed_at, /* Public associations */ _Agency, _Customer, _Currency }
而后建立一个projection view,将该view的字段有选择性地暴露出来。
@EndUserText.label: 'Travel projection view - Processor' @AccessControl.authorizationCheck: #NOT_REQUIRED @UI: { headerInfo: { typeName: 'Travel', typeNamePlural: 'Travels', title: { type: #STANDARD, value: 'TravelID' } } } @Search.searchable: true define root view entity ZC_TRAVEL_JERRY as projection on ZI_TRAVEL_JERRY { @UI.facet: [ { id: 'Travel', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'Travel', position: 10 } ] @UI: { lineItem: [ { position: 10, importance: #HIGH } ], identification: [ { position: 10, label: 'Travel ID [1,...,99999999]' } ] } @Search.defaultSearchElement: true key travel_id as TravelID, @UI: { lineItem: [ { position: 20, importance: #HIGH } ], identification: [ { position: 20 } ], selectionField: [ { position: 20 } ] } @Consumption.valueHelpDefinition: [{ entity : {name: '/DMO/I_Agency', element: 'AgencyID' } }] @ObjectModel.text.element: ['AgencyName'] ----meaning? @Search.defaultSearchElement: true agency_id as AgencyID, _Agency.Name as AgencyName, @UI: { lineItem: [ { position: 30, importance: #HIGH } ], identification: [ { position: 30 } ], selectionField: [ { position: 30 } ] } @Consumption.valueHelpDefinition: [{ entity : {name: '/DMO/I_Customer', element: 'CustomerID' } }] @ObjectModel.text.element: ['CustomerName'] @Search.defaultSearchElement: true customer_id as CustomerID, @UI.hidden: true _Customer.LastName as CustomerName, @UI: { lineItem: [ { position: 40, importance: #MEDIUM } ], identification: [ { position: 40 } ] } begin_date as BeginDate, @UI: { lineItem: [ { position: 41, importance: #MEDIUM } ], identification: [ { position: 41 } ] } end_date as EndDate, @UI: { lineItem: [ { position: 50, importance: #MEDIUM } ], identification: [ { position: 50, label: 'Total Price' } ] } @Semantics.amount.currencyCode: 'CurrencyCode' total_price as TotalPrice, @Consumption.valueHelpDefinition: [{entity: {name: 'I_Currency', element: 'Currency' }}] currency_code as CurrencyCode, @UI.identification: [ { position: 60, label: 'Remarks' } ] description as Description, @UI.hidden: true last_changed_at as LastChangedAt }
你们能够注意到,这个projection view里包含了不少@UI注解,做用和Fiori Elements同样,做为元数据,告诉对应的渲染框架,运行时这些字段应该以什么样的方式渲染在Fiori UI上。
(3) 如今三大支柱之一的Core Data Service已经就位了,接下来咱们基于前一步获得的projection view建立Business Service. 选中projection view,右键选择New Service Definition:
这个服务定义的第一条记录,就是经过ABAP expose关键字把projection view ZC_TRAVEL_JERRY暴露出来,模型名称为TravelProcessor:
@EndUserText.label: 'Service Defintion for ZC_Travel_JERRY' define service ZUI_C_TRAVEL_JERRY { expose ZC_TRAVEL_JERRY as TravelProcessor; expose /DMO/I_Customer as Passenger; expose /DMO/I_Agency as TravelAgency; expose /DMO/I_Airport as Airport; expose I_Currency as Currency; expose I_Country as Country; }
而后基于这个Service Definition建立一个Service Binding,能够简单把Service Binding理解成Service Definition的一个实例:
Service Binding建立完毕后,点击Activate激活:
以前Service Definition里用expose关键字暴露并指定成的模型TravelProcessor此时就可见了,双击:
双击后会自动打开一个连接,一个Fiori应用就呈如今咱们眼前了。咱们没有进行一行的JavaScript web编程,就获得了一个专业的支持高级搜索的Fiori应用,能查看底层数据库表ZTRAVEL_JERRY的内容。
(4) 至此咱们已经了解了Restful ABAP Programming模型的前两大支柱,还剩下Behavior Definition. 既然RAP的口号是打造具备Restful特性的应用,但到目前为止咱们尚未感觉到RAP对Restful的支持,这有待Behavior Definition来完成。
选中以前建立的CDS view,建立一个新的Behavior Definition:
实现类型指定为Managed:
咱们能够看到这个Behavior Definition的定义里,又多了一些新的ABAP关键字。这个Behavior Definition负责定义底层模型的Transaction Behavior,即代码第18到20行的create,update,delete.
固然增删改查的功能光定义不行,还得建立其对应的实现。上图Definition中已经指定了实现这些行为的ABAP类名称为ZCL_BP_I_TRAVEL_M_JERRY. 为此,右键选择New Behavior Implementation:
建立这个特殊的ABAP实现类:
这个实现类里面也不须要开发人员手动编写代码来完成对底层数据库表的增删改查操做——既然能称之为一个编程模型,那么这些通用的功能都经过框架类CL_ABAP_BEHAVIOR_HANDLER统一完成了,应用开发人员只须要定义一个对该类的声明便可。
把这一步建立好的Behavior Definition模型和其实现所有激活,而后回到咱们以前浏览器里打开的Fiori应用,刷新,会发现多了Create和Delete两个按钮,这意味着该应用对建立和删除的支持也已经自动可用了。
同以前的搜索功能同样,这些功能的自动得到,都是创建在应用开发人员一行JavaScript代码都不用编写的基础上的,由此你们感觉到了Restful ABAP Programming模型的强大威力了吗?
后续Jerry会继续介绍如何给这个Fiori应用底层使用的模型增添Action和Validation功能,敬请期待。
要获取更多Jerry的原创文章,请关注公众号"汪子熙":