30分鐘用Restful ABAP Programming模型開發一個支持增刪改查的Fiori應用

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模型的三大支柱:編程

  • Business Service
  • Core Data Service
  • Behavior Definition

下面請跟着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類,而後看到三條數據成功插入了:3d

(2) 咱們最終的目的是建立一個支持對這張表進行增刪改查的Fiori應用,而Restful ABAP Programming模型的三大支柱之一爲Core Data Service,所以咱們首先得有基於數據庫表ZTRAVEL_JERRY的CDS view.code

因此我首先建立一個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功能,敬請期待。

相關文章
相關標籤/搜索