Virtuelle Felder in CDS-Views: Unterschied zwischen den Versionen

Aus SAP Wiki ツ
 
(8 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
[[Kategorie:Core Data Services]]
Öfters ist es notwendig Daten in einem CDS-Views Daten nachzuliefern, dich sich mit einfachen Verbindungen/Beziehungen durch CDS-Views nicht möglich sind und komplexeres Coding notwendig ist. Hierfür bietet vorallen die Berechnunungslogik, um ein Feld mit einem bestimmten Wert zu füllen. Zwecks Performance sollte man so gut es geht sparsam damit umgehen.
Ein Fall bei mir war, dass ich eine App mit RAP entwickelt habe und bis auf ein paar Spalten es komplett über CDS-Views abbilden konnte.
=== Einleitung ===
=== Einleitung ===


Zeile 9: Zeile 15:


Paar Fakten:
Paar Fakten:
* Virtuelle Felder werden nur innerhalb des OData-Frameworks angesprochen. Abfragen des CDS-Views über Open SQL Selects ergeben keine Ergebnisse, sprich die dahinterlegende Klasse mit Code wird nicht durchlaufen
* Virtuelle Felder werden nur innerhalb des OData-Frameworks angesprochen. Abfragen des CDS-Views über Open SQL Selects ergeben keine Ergebnisse, sprich die dahinterlegende Klasse wird nicht durchlaufen
* Virtuelle Felder kennen sich untereinander nicht
* Virtuelle Felder kennen sich untereinander nicht
* Filterlogik auf virtuelle Felder kann nicht angewendet werden und wird explizit im weiteren SADL-Code abgefangen
* Filterlogik auf virtuelle Felder kann nicht angewendet werden und wird explizit im weiteren SADL-Code abgefangen
* Schlüsselfelder können nicht als virtuelle Elemente definiert werden
* Schlüsselfelder können nicht als virtuelle Elemente definiert werden


=== Deklaration in CDS-View ===
=== Deklaration in CDS-View am Beispiel der Berechnungslogik ===
Virtuelle Felder können dort verwendet werden, wo sie unterstützt werden z.B. in EXTEND VIEW ENTITY:
Virtuelle Felder können dort verwendet werden, wo sie unterstützt werden z.B. in EXTEND VIEW ENTITY:


Zeile 108: Zeile 114:
   ENDIF.
   ENDIF.
   
   
   IF line_exists( it_requested_calc_elements[table_line = 'XYZ' ]) .
   IF line_exists( it_requested_calc_elements[table_line = 'XYZ' ] ) .
   INSERT: conv sadl_entity_element( 'field' ) INTO TABLE et_requested_orig_elements.
   INSERT: conv sadl_entity_element( 'field' ) INTO TABLE et_requested_orig_elements.
   ENDIF.
   ENDIF.
Zeile 117: Zeile 123:
==== Sortierlogik ====
==== Sortierlogik ====


<syntaxhighlight lang="abap" line start="1">
CLASS <classname> DEFINITION
CLASS <classname> DEFINITION
   PUBLIC
   PUBLIC
Zeile 148: Zeile 155:
   
   
ENDCLASS.
ENDCLASS.
</syntaxhighlight>


==== Filterlogik ====
==== Filterlogik ====
<syntaxhighlight lang="abap" line start="1">
CLASS <classname> DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .
  PUBLIC SECTION.
    INTERFACES if_sadl_exit_filter_transform.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.
CLASS <classname> IMPLEMENTATION.
METHOD if_sadl_exit_filter_transform~map_atom.
  DATA: lo_condition_manager TYPE REF TO if_sadl_simple_cond_factory,
        lo_filter            TYPE REF TO  if_sadl_simple_cond_element,
  IF iv_element <> '<cds_view_name>'.
      RAISE EXCEPTION TYPE ...
  ENDIF.
lo_condition_manager = cl_sadl_cond_prov_factory_pub=>create_simple_cond_factory( ).
lo_filter->element('FIELD' ).
CASE iv_operator.
  WHEN if_sadl_exit_filter_transform~co_operator-equals( ... ).
  ...
  WHEN if_sadl_exit_filter_transform~co_operator-less_than( ... ).
  ...
  WHEN if_sadl_exit_filter_transform~co_operator-greater_than( ... ).
  ro_condition = lo_filter->greater_than( <parameter> ).
  WHEN OTHERS.
  RAISE EXCEPTION TYPE cx_sadl_exit_filter_not_supp.
  ENDCASE.
ENDMETHOD.
ENDCLASS.
</syntaxhighlight>


=== Nützliche Seiten ===
=== Nützliche Seiten ===
* https://help.sap.com/docs/SAP_NETWEAVER_AS_ABAP_752/cc0c305d2fab47bd808adcad3ca7ee9d/0046aa3f07de4ab0ba69e16eaa7bf12f.html
* https://help.sap.com/docs/SAP_NETWEAVER_AS_ABAP_752/cc0c305d2fab47bd808adcad3ca7ee9d/0046aa3f07de4ab0ba69e16eaa7bf12f.html

Aktuelle Version vom 3. April 2024, 15:11 Uhr


Öfters ist es notwendig Daten in einem CDS-Views Daten nachzuliefern, dich sich mit einfachen Verbindungen/Beziehungen durch CDS-Views nicht möglich sind und komplexeres Coding notwendig ist. Hierfür bietet vorallen die Berechnunungslogik, um ein Feld mit einem bestimmten Wert zu füllen. Zwecks Performance sollte man so gut es geht sparsam damit umgehen.

Ein Fall bei mir war, dass ich eine App mit RAP entwickelt habe und bis auf ein paar Spalten es komplett über CDS-Views abbilden konnte.

Einleitung

Virtuelle Felder werden mit ABAP-Klassen implementiert und stehen seit dem SAP NetWeaver Release 7.51 zur Verfügung.

Es wird zwischen drei Kategorien unterschieden:

  • virtuelle Felder mit eigener Berechnungslogik
  • virtuelle Felder mit eigener Sortierlogik
  • virtuelle Felder mit eigener Filterlogik

Paar Fakten:

  • Virtuelle Felder werden nur innerhalb des OData-Frameworks angesprochen. Abfragen des CDS-Views über Open SQL Selects ergeben keine Ergebnisse, sprich die dahinterlegende Klasse wird nicht durchlaufen
  • Virtuelle Felder kennen sich untereinander nicht
  • Filterlogik auf virtuelle Felder kann nicht angewendet werden und wird explizit im weiteren SADL-Code abgefangen
  • Schlüsselfelder können nicht als virtuelle Elemente definiert werden

Deklaration in CDS-View am Beispiel der Berechnungslogik

Virtuelle Felder können dort verwendet werden, wo sie unterstützt werden z.B. in EXTEND VIEW ENTITY:

define view <CdsConsumptionView>
		as select from <data_source>
{
		...
		@ObjectModel.readOnly: true
		@ObjectModel.virtualElement
		@ObjectModel.virtualElementCalculatedBy: 'ABAP:<code_exit_class>'
		cast( '' as <dtype> preserving type) as <view.element>
		...
}

In Projection Views ist die Notation etwas anders:

define root view entity <CdsProjectionView>
  provider contract transactional_query
  as projection on <data_source>
{
       ...
       @ObjectModel. virtualElementCalculatedBy: 'ABAP:<code_exit_class>'
       virtual <view.element> : abap.<type>
       ...
}

Analog dazu für Filterung und Sortierung, s. nützliche Seiten.

Exit-Klassen

Für die drei möglichen Fällen von virtuellen Elementen muss eine Klasse erstellt werden, die mit einem Marker, d.h. INTERFACE gekennzeichnet wird, sodass zwingend der Methodenkopf genutzt wird, wie sie im INTERFACE vorgegeben wird. Wie der Methodenrumpf ausprogrammiert wird, sieht man wie folgt bei allen Logiken.

Berechnungslogik

Für eine Bonus- und Analyseapp war es notwendig den aktuellen Umsatz zu berechnen. Dies lies sich nicht einfach durch CDS-Views ermitteln und musste berechnet werden.

Ich habe mir angewohnt die Exit-Klasse als Suffix mit _CALC zu bezeichnen.

CDS-View
		...
		@ObjectModel. virtualElementCalculatedBy: 'ABAP:YCL_CCM_AMOUNT_CALC'
		virtual amount              : abap.dec( 13,2 ), //abap.dec( 15,2 ), //wcb_scale
		...
CLASS ycl_ccm_amount_calc DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    INTERFACES if_sadl_exit_calc_element_read.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS ycl_ccm_amount_calc IMPLEMENTATION.

  METHOD if_sadl_exit_calc_element_read~calculate.

    DATA lt_orig_data TYPE STANDARD TABLE OF <cds_view_with_exit_class>.
    FIELD-SYMBOLS <ls_orig_data> TYPE ycds_p_ccm_start .

    MOVE-CORRESPONDING it_original_data TO lt_orig_data.

    LOOP AT lt_orig_data ASSIGNING <ls_orig_data>.

      try.
       <ls_orig_data>-amount = ycl_ccm_app_analy_amoun_helper=>get_current_summed_amount(
                                iv_condition_contract_no = <ls_orig_data>-ConditionContract
                               ).
     catch cx_root into DATA(cx).
       <ls_orig_data>-amount = 0.
      endtry.

    ENDLOOP.
    MOVE-CORRESPONDING lt_orig_data TO ct_calculated_data.

  ENDMETHOD.

Es ist mit der Methode get_calculation_info möglich hier nur die benötigten Elemente hinzuzufügen oder Prüfungen zu machen, ob die richtige CDS-View diese Klasse antriggert.

 METHOD if_sadl_exit_calc_element_read~get_calculation_info.

  IF iv_entity <> '<your_cds_view'.
   RAISE EXCEPTION TYPE ...
  ENDIF.
 
  IF line_exists( it_requested_calc_elements[table_line = 'XYZ' ] ) .
   INSERT: conv sadl_entity_element( 'field' ) INTO TABLE et_requested_orig_elements.
  ENDIF.

 ENDMETHOD.

Sortierlogik

CLASS <classname> DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .
 
  PUBLIC SECTION.
    INTERFACES if_sadl_exit_sort_transform.
 
  PROTECTED SECTION.
 
  PRIVATE SECTION.
 
ENDCLASS.

CLASS <classname> IMPLEMENTATION.
 
  METHOD if_sadl_exit_sort_transform~map_element.
 
    IF iv_entity <> '<cds_view_name>'.
      RAISE EXCEPTION TYPE ...
    ENDIF.
 
    IF iv_element = 'AMOUNT'.
      et_sort_elements = VALUE #(
        ( name = 'AMOUNT' ) ( name = 'CONDITIONCONTRACT' )
      ).
    ENDIF.
 
  ENDMETHOD.
 
ENDCLASS.

Filterlogik

CLASS <classname> DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .
 
  PUBLIC SECTION.
    INTERFACES if_sadl_exit_filter_transform.
 
  PROTECTED SECTION.
 
  PRIVATE SECTION.
 
ENDCLASS.

CLASS <classname> IMPLEMENTATION.
 METHOD if_sadl_exit_filter_transform~map_atom.
  DATA: lo_condition_manager TYPE REF TO if_sadl_simple_cond_factory,
        lo_filter            TYPE REF TO  if_sadl_simple_cond_element,

  IF iv_element <> '<cds_view_name>'.
      RAISE EXCEPTION TYPE ...
  ENDIF.

 lo_condition_manager = cl_sadl_cond_prov_factory_pub=>create_simple_cond_factory( ).
 lo_filter->element('FIELD' ).

 CASE iv_operator.
  WHEN if_sadl_exit_filter_transform~co_operator-equals( ... ).
   ...
  WHEN if_sadl_exit_filter_transform~co_operator-less_than( ... ).
   ...
  WHEN if_sadl_exit_filter_transform~co_operator-greater_than( ... ).
   ro_condition = lo_filter->greater_than( <parameter> ).
  WHEN OTHERS.
   RAISE EXCEPTION TYPE cx_sadl_exit_filter_not_supp.
  ENDCASE.
 ENDMETHOD.
ENDCLASS.

Nützliche Seiten