29. 如何通过 ABAP 代码给 SAP OData 元数据增添注解
笔者的这篇 SAP UI5 开发教程文章,* SAP UI5 应用开发教程之一百二十九 - 如何给 SAP UI5 SmartField 添加 Value Help 功能,有朋友留言:
你好,请教一个问题,请问如何在odata里面通过annotations添加一个自定义注解?
你好,请问可以不可以在odata里面通过什么设置,从而自动给metadata.xml中加上注解,实现value help的功能,还是说每次需要在metadata.xml中修改?
这位朋友实际咨询的就是如何维护 metadata.xml 文件里这种 Annotations 标签。
在 OData 元数据里生成自定义注解的方式有很多种,比如在 CDS view 里维护注解,然后基于 CDS view 生成 OData 服务。对于本系列到目前为止采取 SEGW 事物码开发的 OData 服务来说,我们没有 CDS view 模型,因此可以采取另一种办法,即在 MPC_EXT 类里手动编写 ABAP 代码的方式来维护注解。
我们先看下效果,在 MPC_EXT 类的 DEFINE 方法里编写 ABAP 代码之后,用 Postman 使用 url 访问 OData metadata,在返回的数据里,能看到我们用 ABAP 代码添加的自定义注解。
<Annotations Target="ZJERRY.Book" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Annotation Term="UI.HeaderInfo">
<Record>
<PropertyValue Property="TypeName" String="Book"/>
<PropertyValue Property="TypeNamePlural" String="Books"/>
</Record>
</Annotation>
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Label" String="Book"/>
<PropertyValue Property="Value" Path="Book"/>
</Record>
</Collection>
</Annotation>
</Annotations>
下面是详细的实现步骤讲解。
我们的 MPC_EXT 类里有一个属性 VOCAB_ANNO_MODEL
, 要使用 ABAP 代码创建注解,就需要调用这个属性的 API 方法。
自定义注解的运行时载体是一系列拥有前缀 /iwbep/if_mgw_vocan
的对象实例,这些对象实例的列表,笔者已经在下面的代码里列出来了:
DATA: lo_ann_target TYPE REF TO /iwbep/if_mgw_vocan_ann_target. " Vocabulary Annotation Target
DATA: lo_ann_target2 TYPE REF TO /iwbep/if_mgw_vocan_ann_target. " Vocabulary Annotation Target
DATA: lo_annotation TYPE REF TO /iwbep/if_mgw_vocan_annotation. " Vocabulary Annotation
DATA: lo_collection TYPE REF TO /iwbep/if_mgw_vocan_collection. " Vocabulary Annotation Collection
DATA: lo_function TYPE REF TO /iwbep/if_mgw_vocan_function. " Vocabulary Annotation Function
DATA: lo_fun_param TYPE REF TO /iwbep/if_mgw_vocan_fun_param. " Vocabulary Annotation Function Parameter
DATA: lo_property TYPE REF TO /iwbep/if_mgw_vocan_property. " Vocabulary Annotation Property
DATA: lo_record TYPE REF TO /iwbep/if_mgw_vocan_record. " Vocabulary Annotation Record
DATA: lo_simp_value TYPE REF TO /iwbep/if_mgw_vocan_simple_val. " Vocabulary Annotation Simple Value
DATA: lo_url TYPE REF TO /iwbep/if_mgw_vocan_url. " Vocabulary Annotation URL
DATA: lo_label_elem TYPE REF TO /iwbep/if_mgw_vocan_label_elem. " Vocabulary Annotation Labeled Element
DATA: lo_reference TYPE REF TO /iwbep/if_mgw_vocan_reference. " Vocabulary Annotation Reference
本例我们为 OData 元数据创建一些位于命名空间 com.sap.vocabularies.UI.v1
之下的注解。
lo_reference = vocab_anno_model->create_vocabulary_reference( iv_vocab_id = '/IWBEP/VOC_UI' iv_vocab_version = '0001').
lo_reference->create_include( iv_namespace = 'com.sap.vocabularies.UI.v1' iv_alias = 'UI' ).
上述代码,首先定义了一个指向 id 为 /IWBEP/VOC_UI
的词汇引用,然后为命名空间 com.sap.vocabularies.UI.v1 声明了一个别名 UI
. 这样一来,我们稍后可以直接在代码里使用 UI
的简写形式,来指代 com.sap.vocabularies.UI.v1
了。
上述代码最终效果体现在元数据里如下图所示:
lo_ann_target = vocab_anno_model->create_annotations_target( 'Book' ).
lo_ann_target->set_namespace_qualifier( 'ZJERRY' ). "change the namespace to the SRV namespace
上面的代码,给名叫 Book
的 Entity Type 创建注解,并且设置命名空间为 ZJERRY. 最后在元数据里生成的注解如下图所示:
继续为上图的注解创建更多的元素。
lo_annotation = lo_ann_target->create_annotation( iv_term = 'UI.HeaderInfo' ).
lo_record = lo_annotation->create_record( ).
lo_record->create_property( 'TypeName' )->create_simple_value( )->set_string('Book').
lo_record->create_property( 'TypeNamePlural' )->create_simple_value( )->set_string( 'Books').
为 ZJERRY.Book
这个 Entity Type 创建名叫 UI.HeaderInfo
的注解,然后创建 Record 子节点。在 Record 子节点内,创建名叫 TypeName 和 TypeNamePlural 的属性,分别附上属性值 Book 和 Books.
这些注解都是为 Fiori Elements 的 List Report 页面使用的,在元数据里的效果如下图所示:
剩下的代码原理同已经讲解的相同,这里不再赘述:
lo_annotation = lo_ann_target->create_annotation( iv_term = 'UI.LineItem' ).
lo_collection = lo_annotation->create_collection( ).
lo_record = lo_collection->create_record( iv_record_type = 'UI.DataField' ).
lo_property = lo_record->create_property( 'Label' ).
lo_simp_value = lo_property->create_simple_value( ).
lo_simp_value->set_string( 'Book' ).
lo_property = lo_record->create_property( 'Value' ).
lo_simp_value = lo_property->create_simple_value( ).
lo_simp_value->set_path( 'Book' ).
上述代码在 OData 元数据里产生的效果:
注意,大家把上述代码粘贴到 Define 方法中,激活之后,使用 Postman 发起元数据请求,可能无法看到编写的代码立即生效。
究其原因,是因为这篇文章里介绍的 OData 缓存在起作用:
27. SAP OData 框架里的缓存(Cache)设计专题讲座
只需要按照笔者文章里介绍的步骤,执行 /IWFND/CACHE_CLEANUP
和 /IWBEP/CACHE_CLEANUP
两个事物码清除前台服务器和后台服务器的缓存即可:
Define 方法的完整代码:
method DEFINE.
super->define( ).
DATA(lo_entity_type) = model->get_entity_type( iv_entity_name = 'File' ).
IF lo_entity_type IS BOUND.
lo_entity_type->set_is_media( iv_is_media = abap_true ).
lo_entity_type->get_property( iv_property_name = 'mimetype' )->set_as_content_type( ).
ENDIF.
DATA: lo_ann_target TYPE REF TO /iwbep/if_mgw_vocan_ann_target. " Vocabulary Annotation Target
DATA: lo_ann_target2 TYPE REF TO /iwbep/if_mgw_vocan_ann_target. " Vocabulary Annotation Target
DATA: lo_annotation TYPE REF TO /iwbep/if_mgw_vocan_annotation. " Vocabulary Annotation
DATA: lo_collection TYPE REF TO /iwbep/if_mgw_vocan_collection. " Vocabulary Annotation Collection
DATA: lo_function TYPE REF TO /iwbep/if_mgw_vocan_function. " Vocabulary Annotation Function
DATA: lo_fun_param TYPE REF TO /iwbep/if_mgw_vocan_fun_param. " Vocabulary Annotation Function Parameter
DATA: lo_property TYPE REF TO /iwbep/if_mgw_vocan_property. " Vocabulary Annotation Property
DATA: lo_record TYPE REF TO /iwbep/if_mgw_vocan_record. " Vocabulary Annotation Record
DATA: lo_simp_value TYPE REF TO /iwbep/if_mgw_vocan_simple_val. " Vocabulary Annotation Simple Value
DATA: lo_url TYPE REF TO /iwbep/if_mgw_vocan_url. " Vocabulary Annotation URL
DATA: lo_label_elem TYPE REF TO /iwbep/if_mgw_vocan_label_elem. " Vocabulary Annotation Labeled Element
DATA: lo_reference TYPE REF TO /iwbep/if_mgw_vocan_reference. " Vocabulary Annotation Reference
lo_reference = vocab_anno_model->create_vocabulary_reference( iv_vocab_id = '/IWBEP/VOC_UI' iv_vocab_version = '0001').
lo_reference->create_include( iv_namespace = 'com.sap.vocabularies.UI.v1' iv_alias = 'UI' ).
lo_ann_target = vocab_anno_model->create_annotations_target( 'Book' ).
lo_ann_target->set_namespace_qualifier( 'ZJERRY' ). "change the namespace to the SRV namespace
lo_annotation = lo_ann_target->create_annotation( iv_term = 'UI.HeaderInfo' ).
lo_record = lo_annotation->create_record( ).
lo_record->create_property( 'TypeName' )->create_simple_value( )->set_string('Book').
lo_record->create_property( 'TypeNamePlural' )->create_simple_value( )->set_string( 'Books').
lo_annotation = lo_ann_target->create_annotation( iv_term = 'UI.LineItem' ).
lo_collection = lo_annotation->create_collection( ).
lo_record = lo_collection->create_record( iv_record_type = 'UI.DataField' ).
lo_property = lo_record->create_property( 'Label' ).
lo_simp_value = lo_property->create_simple_value( ).
lo_simp_value->set_string( 'Book' ).
lo_property = lo_record->create_property( 'Value' ).
lo_simp_value = lo_property->create_simple_value( ).
lo_simp_value->set_path( 'Book' ).
endmethod.