大数跨境
0
0

【学习系列】SAP RAP 23:Unmanaged Query-Custom Entity开发示例(读取、事务交互)

【学习系列】SAP RAP 23:Unmanaged Query-Custom Entity开发示例(读取、事务交互) DeveloperMrMeng
2025-12-04
3

 

前言

在之前的文档中已经学习总结了managed、unmanaged场景下关于事务操作的基本用例,以及一些扩展用例,本篇介绍下unmanaged query下的实现方式:Custom Entity

unmanaged query是通过实现特定ABAP接口来实现对持久或者非持久的只读访问,当查询框架的标准SQL下推不足以支撑复杂数据的查询时,或者无法直接从持久数据源获取时,可以选择这种方式来实现查询,其实可以把Custom Entity理解为经典的ABAP ALV报表,当取数逻辑非常复杂,无法直接通过视图关联以及无法通过标准的CDS函数得到结果的,比如某些字段是需要通过调用类或者函数,甚至是从第三方服务获取的,则使用Custom Entity是最适合的选择。


正文

本文以一个实际的例子来说明Custom Entity的基本使用示例,主要将包含两个部分:

  1. 1. 如何实现基本的查询接口
  2. 2. 如何实现行为定义来完成数据更新
  3. 3. 如何将OData V2默认的单选框改为多选框

测试环境为S4 HANA Cloud Public Edition,所有的完整示例代码在文末统一提供。

下图是unmanaged query的运行时示例图:

可以看到实现方式和之前的方式略有不同,需要使用custom entity来定义查询实体,通过实现接口IF_RAP_QUERY_PROVIDER来实现具体的查询逻辑,所以接下来按照此步骤来完成具体的开发,还是用一个具有父子层级的实体作为示例进行演示说明,这样可以和之前的文章有对比,也能对unmanaged的实现过程有进一步理解。


创建基本实体

1.创建根实体

可以选择将所有的UI注解直接在custom entity中添加,也可以选择单独创建Metadata Extension,这样UI层面的注解单独分离,定义数据的实体看起来会更清爽一些。

1. @EndUserText.label: 'Unmanaged query demo head'
2. @ObjectModel.query.implementedBy: 'ABAP:ZCL_RAP_DEMO_CUSTOM'
3. @Metadata.allowExtensions: true
4. define custom entity zi_rap_demo_custom_head
5. {
6. key purchaseorder          : ebeln;
7. purchaseordertype      : abap.char( 4 );
8. creationdate           : aedat;
9. supplier               : lifnr;
10. language               : spras;
11. purchasingorganization : ekorg;
12. purchasinggroup        : ekgrp;
13. documentcurrency       : waers;
14. status                 : abap.char(10);
15. }

2.创建实施类

必须实现接口if_rap_query_provider


3.创建根实体UI扩展

1. @Metadata.layer: #CUSTOMER
2. @UI: {
3. headerInfo: {
4. typeName: 'Purchase Order',
5. typeNamePlural: 'Purchase Orders',
6. title: { value: 'purchaseorder' }
7. },
8. presentationVariant: [{
9. sortOrder: [ { by: 'purchaseorder', direction:  #ASC } ],
10. maxItems: 1000,
11. visualizations : [{ type: #AS_LINEITEM }]
12. }]
13. }
14. annotate entity zi_rap_demo_custom_head with
15. {
16. @UI.facet              : [
17. {
18. id             : 'Head',
19. purpose        : #STANDARD,
20. position       : 10,
21. type           :#IDENTIFICATION_REFERENCE,
22. label          : 'Head'
23. }
24. ]
25. 
26. @UI.lineItem           : [{ position: 10 }]
27. @UI.identification     : [{ position: 10 }]
28. @UI.selectionField     : [{ position: 10 }]
29. purchaseorder;
30. 
31. @UI.lineItem           : [{ position: 20 }]
32. @UI.identification     : [{ position: 20 }]
33. @UI.selectionField     : [{ position: 20 }]
34. purchaseordertype;
35. 
36. @UI.lineItem           : [{ position: 30 }]
37. @UI.identification     : [{ position: 30 }]
38. @UI.selectionField     : [{ position: 30 }]
39. creationdate;
40. 
41. @UI.lineItem           : [{ position: 40 }]
42. @UI.identification     : [{ position: 40 }]
43. @UI.selectionField     : [{ position: 40 }]
44. supplier;
45. 
46. @UI.lineItem           : [{ position: 50 }]
47. @UI.identification     : [{ position: 50 }]
48. language;
49. 
50. @UI.lineItem           : [{ position: 60 }]
51. @UI.identification     : [{ position: 60 }]
52. @UI.selectionField     : [{ position: 50 }]
53. purchasingorganization;
54. 
55. @UI.lineItem           : [{ position: 70 }]
56. @UI.identification     : [{ position: 70 }]
57. @UI.selectionField     : [{ position: 60 }]
58. purchasinggroup;
59. 
60. @UI.lineItem           : [{ position: 80 }]
61. @UI.identification     : [{ position: 80 }]
62. documentcurrency;
63. 
64. @EndUserText.label     : 'Status'
65. @UI.lineItem           : [{ position: 90 }]
66. @UI.identification     : [{ position: 90 }]
67. status;
68. }

4.创建子实体

注意需要通过association to parent关联父实体,实施类这里选择和父实体同一个实施类即可:

1. @EndUserText.label: 'Unmanaged query demo item'
2. @ObjectModel.query.implementedBy: 'ABAP:ZCL_RAP_DEMO_CUSTOM'
3. @Metadata.allowExtensions: true
4. define custom entity zi_rap_demo_custom_item
5. {
6. key purchaseorder             : ebeln;
7. key PurchaseOrderItem         : ebelp;
8. Material                  : matnr;
9. PurchaseOrderItemText     : txz01;
10. @Semantics.quantity.unitOfMeasure: 'PurchaseOrderQuantityUnit'
11. OrderQuantity             : abap.quan( 13, 2 );
12. PurchaseOrderQuantityUnit : bstme;
13. @Semantics.amount.currencyCode: 'DocumentCurrency'
14. NetAmount                 : abap.curr( 13, 2 );
15. DocumentCurrency          : waers;
16. _head                     : association to parent zi_rap_demo_custom_head on $projection.purchaseorder = _head.purchaseorder;
17. }

同时父实体中也需要关联子实体,父实体需要添加root关键字,因为后续要创建对应的行为定义:

_item                  : composition [0..*] of zi_rap_demo_custom_item;

5.创建子实体UI扩展

1. @Metadata.layer: #CUSTOMER
2. @UI: {
3. headerInfo: {
4. typeName: 'Purchase Order Item',
5. typeNamePlural: 'Purchase Order Items',
6. title: { value: 'PurchaseOrderItem' }
7. },
8. presentationVariant: [{
9. sortOrder: [ { by: 'purchaseorder', direction:  #ASC },
10. { by: 'PurchaseOrderItem', direction:  #ASC } ],
11. maxItems: 1000,
12. visualizations : [{ type: #AS_LINEITEM }]
13. }]
14. }
15. annotate entity zi_rap_demo_custom_item with
16. {
17. @UI.facet                 : [
18. {
19. id                  : 'Items_Detail',
20. purpose             : #STANDARD,
21. position            : 10,
22. type                :#IDENTIFICATION_REFERENCE,
23. label               : 'Items Detail'
24. }
25. ]
26. @UI.lineItem              : [{ position: 10 }]
27. @UI.identification        : [{ position: 10 }]
28. purchaseorder;
29. 
30. @UI.lineItem              : [{ position: 20 }]
31. @UI.identification        : [{ position: 20 }]
32. PurchaseOrderItem;
33. 
34. @UI.lineItem              : [{ position: 30 }]
35. @UI.identification        : [{ position: 30 }]
36. Material;
37. 
38. @UI.lineItem              : [{ position: 40 }]
39. @UI.identification        : [{ position: 40 }]
40. PurchaseOrderItemText;
41. 
42. @UI.lineItem              : [{ position: 50 }]
43. @UI.identification        : [{ position: 50 }]
44. OrderQuantity;
45. 
46. @UI.lineItem              : [{ position: 60 }]
47. @UI.identification        : [{ position: 60 }]
48. PurchaseOrderQuantityUnit;
49. 
50. @UI.lineItem              : [{ position: 70 }]
51. @UI.identification        : [{ position: 70 }]
52. NetAmount;
53. 
54. @UI.lineItem              : [{ position: 80 }]
55. @UI.identification        : [{ position: 80 }]
56. DocumentCurrency;
57. 
58. }

更新根实体UI扩展,添加子实体的导航

1. {
2. id             :'Item',
3. purpose        : #STANDARD,
4. type           :#LINEITEM_REFERENCE,
5. position       :20,
6. targetElement  : '_item',
7. label          :'Item'
8. }

6.创建服务定义

1. @EndUserText.label: 'Service Definition for Custom Entity'
2. @ObjectModel.leadingEntity.name: 'zi_rap_demo_custom_head'
3. define service zsrvd_rap_demo_custom {
4. expose zi_rap_demo_custom_head as head;
5. expose zi_rap_demo_custom_item as item;
6. }

7.创建服务绑定

这里选择了OData V2,因为后续的示例步骤中想使用标准的编辑功能,OData V4要启用编辑必须启用草稿,而custom entity不支持草稿,所以想同时使用标准的action时只能使用V2。

公有云环境首次激活后一般直接打不开,没事等一会就好,先做后续的步骤:


实现具体查询逻辑

类型和方法定义

因为这里我们有两个实体,所以我们将核心差异的取数逻辑单独封装在各自的方法中进行实现:

  • • 目的1是为了分离取数逻辑
  • • 目的2是为了后续在行为的实现方法中复用

实现select方法

这里使用的是比较通用的一些方法和步骤,下面简单说明一下:

1.获取筛选条件
1. "Get filter ranges
2. TRY.
3. DATA(lt_ranges) = io_request->get_filter( )->get_as_ranges( ).
4. CATCH cx_rap_query_filter_no_range.
5. "Handle error
6. ENDTRY.

get_as_ranges方法实际上是将界面上实际输入的选择条件转换为range表,类比于ALV程序中的select-option:

如果使用get_as_sql_string则转换效果如下,使用range表比较符合以前开发传统报表的习惯。

2.获取分页大小
1. "Get offset and page size
2. DATA(lv_offset)    = io_request->get_paging( )->get_offset( ).
3. DATA(lv_page_size) = io_request->get_paging( )->get_page_size( ).
4. DATA(lv_max_rows)  = COND #( WHEN lv_page_size = if_rap_query_paging=>page_size_unlimited
5. THEN 0 ELSE lv_page_size ).

【声明】内容源于网络
0
0
DeveloperMrMeng
从事SAP开发相关工作多年,不定时更新一些技术总结,佛系更文,如果觉得有用不妨一键三连😁
内容 67
粉丝 0
DeveloperMrMeng 从事SAP开发相关工作多年,不定时更新一些技术总结,佛系更文,如果觉得有用不妨一键三连😁
总阅读25
粉丝0
内容67