ABAP Dynamic Programming: RTTS, CREATE DATA, Reflection

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Dynamic Programming ermöglicht die Erstellung und Manipulation von Datenobjekten zur Laufzeit. Mit RTTS (Runtime Type Services) und dynamischen Anweisungen können flexible, generische Programme entwickelt werden.

RTTS-Klassen

KlasseBeschreibung
CL_ABAP_TYPEDESCRBasisklasse für Typbeschreibungen
CL_ABAP_ELEMDESCRElementare Typen (i, string, etc.)
CL_ABAP_STRUCTDESCRStrukturen
CL_ABAP_TABLEDESCRInterne Tabellen
CL_ABAP_REFDESCRReferenzen
CL_ABAP_CLASSDESCRKlassen
CL_ABAP_OBJECTDESCRObjekte

Beispiele

1. Typ eines Datenobjekts ermitteln

DATA: lv_string TYPE string VALUE 'Test',
lt_table TYPE TABLE OF mara,
lo_struct TYPE REF TO kna1.
" Typ ermitteln
DATA(lo_type1) = cl_abap_typedescr=>describe_by_data( lv_string ).
DATA(lo_type2) = cl_abap_typedescr=>describe_by_data( lt_table ).
DATA(lo_type3) = cl_abap_typedescr=>describe_by_name( 'KNA1' ).
WRITE: / 'String Typ:', lo_type1->type_kind,
/ 'Tabelle Typ:', lo_type2->type_kind,
/ 'KNA1 Typ:', lo_type3->type_kind.
" type_kind Werte:
" C = Character, N = Numeric, I = Integer, P = Packed
" D = Date, T = Time, X = Hex, F = Float
" g = String, h = Table, u = Structure, v = Class

2. Strukturkomponenten lesen

DATA: ls_customer TYPE kna1.
" Struktur beschreiben
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_data( ls_customer )
).
" Alle Komponenten ausgeben
LOOP AT lo_struct->components INTO DATA(ls_comp).
WRITE: / ls_comp-name, ls_comp-type_kind, ls_comp-length.
ENDLOOP.
" Bestimmte Komponente suchen
READ TABLE lo_struct->components INTO ls_comp
WITH KEY name = 'KUNNR'.
IF sy-subrc = 0.
WRITE: / 'KUNNR gefunden, Länge:', ls_comp-length.
ENDIF.

3. Dynamische Struktur erstellen

" Komponenten definieren
DATA(lt_components) = VALUE cl_abap_structdescr=>component_table(
( name = 'FIELD1' type = cl_abap_elemdescr=>get_c( 10 ) )
( name = 'FIELD2' type = cl_abap_elemdescr=>get_i( ) )
( name = 'FIELD3' type = cl_abap_elemdescr=>get_string( ) )
( name = 'FIELD4' type = cl_abap_elemdescr=>get_d( ) )
).
" Strukturtyp erstellen
DATA(lo_struct_type) = cl_abap_structdescr=>create( lt_components ).
" Datenobjekt erstellen
DATA: lr_struct TYPE REF TO data.
CREATE DATA lr_struct TYPE HANDLE lo_struct_type.
" Mit Daten füllen
ASSIGN lr_struct->* TO FIELD-SYMBOL(<fs_struct>).
ASSIGN COMPONENT 'FIELD1' OF STRUCTURE <fs_struct> TO FIELD-SYMBOL(<fv_field1>).
<fv_field1> = 'Hallo'.
ASSIGN COMPONENT 'FIELD2' OF STRUCTURE <fs_struct> TO FIELD-SYMBOL(<fv_field2>).
<fv_field2> = 42.

4. Dynamische interne Tabelle erstellen

" Strukturtyp (wie oben)
DATA(lt_components) = VALUE cl_abap_structdescr=>component_table(
( name = 'KUNNR' type = cl_abap_elemdescr=>get_c( 10 ) )
( name = 'NAME1' type = cl_abap_elemdescr=>get_c( 35 ) )
( name = 'AMOUNT' type = cl_abap_elemdescr=>get_p( p_length = 8 p_decimals = 2 ) )
).
DATA(lo_struct_type) = cl_abap_structdescr=>create( lt_components ).
" Tabellentyp erstellen
DATA(lo_table_type) = cl_abap_tabledescr=>create(
p_line_type = lo_struct_type
p_table_kind = cl_abap_tabledescr=>tablekind_std
).
" Tabelle erstellen
DATA: lr_table TYPE REF TO data.
CREATE DATA lr_table TYPE HANDLE lo_table_type.
ASSIGN lr_table->* TO FIELD-SYMBOL(<ft_table>).
" Zeile hinzufügen
DATA: lr_line TYPE REF TO data.
CREATE DATA lr_line TYPE HANDLE lo_struct_type.
ASSIGN lr_line->* TO FIELD-SYMBOL(<fs_line>).
ASSIGN COMPONENT 'KUNNR' OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<fv>).
<fv> = '0000001000'.
ASSIGN COMPONENT 'NAME1' OF STRUCTURE <fs_line> TO <fv>.
<fv> = 'Test Customer'.
ASSIGN COMPONENT 'AMOUNT' OF STRUCTURE <fs_line> TO <fv>.
<fv> = '1234.56'.
INSERT <fs_line> INTO TABLE <ft_table>.

5. Dynamisches SELECT

DATA: lv_tablename TYPE tabname VALUE 'KNA1',
lv_fields TYPE string VALUE 'KUNNR NAME1 ORT01',
lv_where TYPE string VALUE 'LAND1 = ''DE''',
lr_data TYPE REF TO data.
" Tabellenstruktur ermitteln
DATA(lo_table_descr) = CAST cl_abap_tabledescr(
cl_abap_tabledescr=>describe_by_name( lv_tablename )
).
DATA(lo_line_type) = lo_table_descr->get_table_line_type( ).
" Dynamische Tabelle erstellen
DATA(lo_dyn_table) = cl_abap_tabledescr=>create( lo_line_type ).
CREATE DATA lr_data TYPE HANDLE lo_dyn_table.
ASSIGN lr_data->* TO FIELD-SYMBOL(<ft_result>).
" Dynamisches SELECT
SELECT (lv_fields)
FROM (lv_tablename)
WHERE (lv_where)
INTO CORRESPONDING FIELDS OF TABLE @<ft_result>
UP TO 100 ROWS.
" Ergebnis verarbeiten
LOOP AT <ft_result> ASSIGNING FIELD-SYMBOL(<fs_row>).
ASSIGN COMPONENT 'KUNNR' OF STRUCTURE <fs_row> TO FIELD-SYMBOL(<fv_kunnr>).
ASSIGN COMPONENT 'NAME1' OF STRUCTURE <fs_row> TO FIELD-SYMBOL(<fv_name>).
WRITE: / <fv_kunnr>, <fv_name>.
ENDLOOP.

6. Generische Feldliste

CLASS zcl_dynamic_fields DEFINITION.
PUBLIC SECTION.
METHODS: read_table_dynamic
IMPORTING iv_table TYPE tabname
it_fields TYPE string_table
iv_where TYPE string OPTIONAL
EXPORTING et_data TYPE REF TO data.
ENDCLASS.
CLASS zcl_dynamic_fields IMPLEMENTATION.
METHOD read_table_dynamic.
" Feldliste als String
DATA(lv_fields) = concat_lines_of( table = it_fields sep = ` ` ).
" Struktur nur mit gewünschten Feldern erstellen
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( iv_table )
).
DATA(lt_components) = VALUE cl_abap_structdescr=>component_table( ).
LOOP AT it_fields INTO DATA(lv_field).
READ TABLE lo_struct->components INTO DATA(ls_comp)
WITH KEY name = to_upper( lv_field ).
IF sy-subrc = 0.
APPEND VALUE #(
name = ls_comp-name
type = lo_struct->get_component_type( ls_comp-name )
) TO lt_components.
ENDIF.
ENDLOOP.
" Dynamische Tabelle erstellen
DATA(lo_new_struct) = cl_abap_structdescr=>create( lt_components ).
DATA(lo_new_table) = cl_abap_tabledescr=>create( lo_new_struct ).
CREATE DATA et_data TYPE HANDLE lo_new_table.
ASSIGN et_data->* TO FIELD-SYMBOL(<ft_data>).
" SELECT
IF iv_where IS INITIAL.
SELECT (lv_fields) FROM (iv_table)
INTO CORRESPONDING FIELDS OF TABLE @<ft_data>.
ELSE.
SELECT (lv_fields) FROM (iv_table)
WHERE (iv_where)
INTO CORRESPONDING FIELDS OF TABLE @<ft_data>.
ENDIF.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lr_result TYPE REF TO data.
NEW zcl_dynamic_fields( )->read_table_dynamic(
EXPORTING
iv_table = 'MARA'
it_fields = VALUE #( ( `MATNR` ) ( `MTART` ) ( `MATKL` ) )
iv_where = 'MTART = ''FERT'''
IMPORTING
et_data = lr_result
).
ASSIGN lr_result->* TO FIELD-SYMBOL(<ft_materials>).

7. Dynamische Objektinstanziierung

DATA: lv_classname TYPE seoclsname VALUE 'ZCL_MY_CLASS',
lo_object TYPE REF TO object.
" Klasse dynamisch instanziieren
CREATE OBJECT lo_object TYPE (lv_classname).
" Mit Parametern
DATA: lt_params TYPE abap_parmbind_tab.
lt_params = VALUE #(
( name = 'IV_PARAM1' kind = cl_abap_objectdescr=>exporting value = REF #( 'Wert1' ) )
( name = 'IV_PARAM2' kind = cl_abap_objectdescr=>exporting value = REF #( 123 ) )
).
CREATE OBJECT lo_object TYPE (lv_classname)
PARAMETER-TABLE lt_params.

8. Dynamischer Methodenaufruf

DATA: lo_object TYPE REF TO object,
lv_method TYPE string VALUE 'PROCESS_DATA',
lt_params TYPE abap_parmbind_tab,
lt_results TYPE abap_parmbind_tab,
lv_input TYPE string VALUE 'Test',
lv_output TYPE string.
" Objekt erstellen
CREATE OBJECT lo_object TYPE zcl_processor.
" Parameter aufbauen
lt_params = VALUE #(
( name = 'IV_INPUT'
kind = cl_abap_objectdescr=>exporting
value = REF #( lv_input ) )
).
lt_results = VALUE #(
( name = 'RV_OUTPUT'
kind = cl_abap_objectdescr=>returning
value = REF #( lv_output ) )
).
" Methode aufrufen
CALL METHOD lo_object->(lv_method)
PARAMETER-TABLE lt_params
EXCEPTION-TABLE lt_results.
WRITE: / 'Ergebnis:', lv_output.

9. Klassenmethoden und Attribute analysieren

DATA: lv_classname TYPE seoclsname VALUE 'CL_ABAP_TYPEDESCR'.
" Klassenbeschreibung laden
DATA(lo_class) = CAST cl_abap_classdescr(
cl_abap_typedescr=>describe_by_name( lv_classname )
).
" Methoden auflisten
WRITE: / 'Methoden:'.
LOOP AT lo_class->methods INTO DATA(ls_method).
WRITE: / '-', ls_method-name, ls_method-visibility.
ENDLOOP.
" Attribute auflisten
WRITE: / 'Attribute:'.
LOOP AT lo_class->attributes INTO DATA(ls_attr).
WRITE: / '-', ls_attr-name, ls_attr-type_kind.
ENDLOOP.
" Interfaces
WRITE: / 'Interfaces:'.
LOOP AT lo_class->interfaces INTO DATA(ls_intf).
WRITE: / '-', ls_intf-name.
ENDLOOP.

10. Generische Datenkonvertierung

CLASS zcl_data_converter DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: convert_itab_to_string_table
IMPORTING it_data TYPE ANY TABLE
RETURNING VALUE(rt_result) TYPE string_table.
CLASS-METHODS: structure_to_json
IMPORTING is_data TYPE any
RETURNING VALUE(rv_json) TYPE string.
ENDCLASS.
CLASS zcl_data_converter IMPLEMENTATION.
METHOD convert_itab_to_string_table.
" Struktur analysieren
DATA(lo_table) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( it_data )
).
DATA(lo_struct) = CAST cl_abap_structdescr(
lo_table->get_table_line_type( )
).
" Jede Zeile in String konvertieren
LOOP AT it_data ASSIGNING FIELD-SYMBOL(<fs_row>).
DATA(lv_line) = ``.
LOOP AT lo_struct->components INTO DATA(ls_comp).
ASSIGN COMPONENT ls_comp-name OF STRUCTURE <fs_row>
TO FIELD-SYMBOL(<fv_value>).
IF lv_line IS NOT INITIAL.
lv_line = lv_line && `;`.
ENDIF.
lv_line = lv_line && <fv_value>.
ENDLOOP.
APPEND lv_line TO rt_result.
ENDLOOP.
ENDMETHOD.
METHOD structure_to_json.
" Einfache JSON-Konvertierung
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_data( is_data )
).
rv_json = `{`.
DATA(lv_first) = abap_true.
LOOP AT lo_struct->components INTO DATA(ls_comp).
ASSIGN COMPONENT ls_comp-name OF STRUCTURE is_data
TO FIELD-SYMBOL(<fv_value>).
IF lv_first = abap_false.
rv_json = rv_json && `,`.
ENDIF.
lv_first = abap_false.
rv_json = rv_json && |"{ to_lower( ls_comp-name ) }":"|.
rv_json = rv_json && <fv_value>.
rv_json = rv_json && `"`.
ENDLOOP.
rv_json = rv_json && `}`.
ENDMETHOD.
ENDCLASS.

11. Dynamische WHERE-Klausel erstellen

CLASS zcl_where_builder DEFINITION.
PUBLIC SECTION.
METHODS: add_condition
IMPORTING iv_field TYPE string
iv_operator TYPE string
iv_value TYPE any
RETURNING VALUE(ro_self) TYPE REF TO zcl_where_builder.
METHODS: add_range
IMPORTING iv_field TYPE string
it_range TYPE ANY TABLE
RETURNING VALUE(ro_self) TYPE REF TO zcl_where_builder.
METHODS: get_where
RETURNING VALUE(rv_where) TYPE string.
PRIVATE SECTION.
DATA: mt_conditions TYPE string_table.
ENDCLASS.
CLASS zcl_where_builder IMPLEMENTATION.
METHOD add_condition.
DATA(lv_condition) = |{ iv_field } { iv_operator } '{ iv_value }'|.
APPEND lv_condition TO mt_conditions.
ro_self = me.
ENDMETHOD.
METHOD add_range.
DATA: lv_condition TYPE string.
LOOP AT it_range ASSIGNING FIELD-SYMBOL(<fs_range>).
ASSIGN COMPONENT 'SIGN' OF STRUCTURE <fs_range> TO FIELD-SYMBOL(<fv_sign>).
ASSIGN COMPONENT 'OPTION' OF STRUCTURE <fs_range> TO FIELD-SYMBOL(<fv_option>).
ASSIGN COMPONENT 'LOW' OF STRUCTURE <fs_range> TO FIELD-SYMBOL(<fv_low>).
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <fs_range> TO FIELD-SYMBOL(<fv_high>).
CASE <fv_option>.
WHEN 'EQ'.
lv_condition = |{ iv_field } = '{ <fv_low> }'|.
WHEN 'BT'.
lv_condition = |{ iv_field } BETWEEN '{ <fv_low> }' AND '{ <fv_high> }'|.
WHEN 'CP'.
lv_condition = |{ iv_field } LIKE '{ <fv_low> }'|.
ENDCASE.
IF <fv_sign> = 'E'.
lv_condition = |NOT ( { lv_condition } )|.
ENDIF.
APPEND lv_condition TO mt_conditions.
ENDLOOP.
ro_self = me.
ENDMETHOD.
METHOD get_where.
rv_where = concat_lines_of( table = mt_conditions sep = ` AND ` ).
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA(lo_where) = NEW zcl_where_builder( ).
DATA(lv_where) = lo_where->add_condition(
iv_field = 'LAND1'
iv_operator = '='
iv_value = 'DE'
)->add_condition(
iv_field = 'KTOKD'
iv_operator = '='
iv_value = '0001'
)->get_where( ).
" Ergebnis: LAND1 = 'DE' AND KTOKD = '0001'

12. Generische Tabellenkopie

CLASS zcl_table_copy DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: copy_matching_fields
IMPORTING it_source TYPE ANY TABLE
CHANGING ct_target TYPE ANY TABLE.
ENDCLASS.
CLASS zcl_table_copy IMPLEMENTATION.
METHOD copy_matching_fields.
" Zielstruktur analysieren
DATA(lo_target_table) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( ct_target )
).
DATA(lo_target_struct) = CAST cl_abap_structdescr(
lo_target_table->get_table_line_type( )
).
" Quellstruktur analysieren
DATA(lo_source_table) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( it_source )
).
DATA(lo_source_struct) = CAST cl_abap_structdescr(
lo_source_table->get_table_line_type( )
).
" Matching Felder ermitteln
DATA(lt_matching) = VALUE string_table( ).
LOOP AT lo_target_struct->components INTO DATA(ls_target_comp).
READ TABLE lo_source_struct->components
WITH KEY name = ls_target_comp-name
TRANSPORTING NO FIELDS.
IF sy-subrc = 0.
APPEND ls_target_comp-name TO lt_matching.
ENDIF.
ENDLOOP.
" Daten kopieren
DATA: lr_target_line TYPE REF TO data.
CREATE DATA lr_target_line LIKE LINE OF ct_target.
ASSIGN lr_target_line->* TO FIELD-SYMBOL(<fs_target_line>).
LOOP AT it_source ASSIGNING FIELD-SYMBOL(<fs_source_line>).
CLEAR <fs_target_line>.
LOOP AT lt_matching INTO DATA(lv_field).
ASSIGN COMPONENT lv_field OF STRUCTURE <fs_source_line>
TO FIELD-SYMBOL(<fv_source>).
ASSIGN COMPONENT lv_field OF STRUCTURE <fs_target_line>
TO FIELD-SYMBOL(<fv_target>).
<fv_target> = <fv_source>.
ENDLOOP.
INSERT <fs_target_line> INTO TABLE ct_target.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

13. Dynamische Funktionsbausteinaufrufe

DATA: lv_funcname TYPE funcname VALUE 'BAPI_CUSTOMER_GETLIST',
lt_params TYPE abap_func_parmbind_tab,
lt_excps TYPE abap_func_excpbind_tab.
" Parameter vorbereiten
DATA: lt_addressdata TYPE bapi1006_address_t,
lt_return TYPE bapiret2_t.
lt_params = VALUE #(
( name = 'ADDRESSDATA'
kind = abap_func_tables
value = REF #( lt_addressdata ) )
( name = 'RETURN'
kind = abap_func_tables
value = REF #( lt_return ) )
).
lt_excps = VALUE #(
( name = 'OTHERS' value = 99 )
).
" Dynamischer Aufruf
CALL FUNCTION lv_funcname
PARAMETER-TABLE lt_params
EXCEPTION-TABLE lt_excps.
IF sy-subrc = 0.
" Erfolg
ENDIF.

14. RTTS für CDS Views

" CDS View Struktur analysieren
DATA(lv_cds_view) = 'ZI_CUSTOMER'.
DATA(lo_cds_type) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( lv_cds_view )
).
" Felder ausgeben
LOOP AT lo_cds_type->components INTO DATA(ls_cds_comp).
WRITE: / ls_cds_comp-name, ls_cds_comp-type_kind.
ENDLOOP.
" Dynamisch SELECT auf CDS View
DATA: lr_cds_data TYPE REF TO data.
DATA(lo_cds_table) = cl_abap_tabledescr=>create( lo_cds_type ).
CREATE DATA lr_cds_data TYPE HANDLE lo_cds_table.
ASSIGN lr_cds_data->* TO FIELD-SYMBOL(<ft_cds_data>).
SELECT * FROM (lv_cds_view)
INTO TABLE @<ft_cds_data>
UP TO 100 ROWS.

15. Generischer Report-Generator

CLASS zcl_generic_report DEFINITION.
PUBLIC SECTION.
METHODS: generate_report
IMPORTING iv_table TYPE tabname
it_fields TYPE string_table
iv_where TYPE string OPTIONAL
iv_max_rows TYPE i DEFAULT 1000
RETURNING VALUE(rt_output) TYPE string_table.
ENDCLASS.
CLASS zcl_generic_report IMPLEMENTATION.
METHOD generate_report.
DATA: lr_data TYPE REF TO data.
" Dynamische Tabelle erstellen
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( iv_table )
).
" Nur gewünschte Felder
DATA(lt_components) = VALUE cl_abap_structdescr=>component_table( ).
LOOP AT it_fields INTO DATA(lv_field).
DATA(lo_comp_type) = lo_struct->get_component_type( to_upper( lv_field ) ).
IF lo_comp_type IS BOUND.
APPEND VALUE #(
name = to_upper( lv_field )
type = lo_comp_type
) TO lt_components.
ENDIF.
ENDLOOP.
IF lt_components IS INITIAL.
RETURN.
ENDIF.
DATA(lo_new_struct) = cl_abap_structdescr=>create( lt_components ).
DATA(lo_new_table) = cl_abap_tabledescr=>create( lo_new_struct ).
CREATE DATA lr_data TYPE HANDLE lo_new_table.
ASSIGN lr_data->* TO FIELD-SYMBOL(<ft_data>).
" Daten lesen
DATA(lv_fields) = concat_lines_of( table = it_fields sep = ` ` ).
IF iv_where IS INITIAL.
SELECT (lv_fields) FROM (iv_table)
INTO CORRESPONDING FIELDS OF TABLE @<ft_data>
UP TO @iv_max_rows ROWS.
ELSE.
SELECT (lv_fields) FROM (iv_table)
WHERE (iv_where)
INTO CORRESPONDING FIELDS OF TABLE @<ft_data>
UP TO @iv_max_rows ROWS.
ENDIF.
" Header erstellen
DATA(lv_header) = concat_lines_of( table = it_fields sep = `|` ).
APPEND lv_header TO rt_output.
" Datenzeilen
LOOP AT <ft_data> ASSIGNING FIELD-SYMBOL(<fs_row>).
DATA(lv_line) = ``.
LOOP AT lt_components INTO DATA(ls_comp).
ASSIGN COMPONENT ls_comp-name OF STRUCTURE <fs_row>
TO FIELD-SYMBOL(<fv_value>).
IF lv_line IS NOT INITIAL.
lv_line = lv_line && `|`.
ENDIF.
lv_line = lv_line && <fv_value>.
ENDLOOP.
APPEND lv_line TO rt_output.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA(lt_report) = NEW zcl_generic_report( )->generate_report(
iv_table = 'KNA1'
it_fields = VALUE #( ( `KUNNR` ) ( `NAME1` ) ( `ORT01` ) ( `LAND1` ) )
iv_where = 'LAND1 = ''DE'''
).
LOOP AT lt_report INTO DATA(lv_line).
WRITE: / lv_line.
ENDLOOP.

Wichtige Hinweise / Best Practice

  • RTTS für Typinformationen zur Laufzeit nutzen.
  • CREATE DATA TYPE HANDLE für dynamische Datenobjekte.
  • ASSIGN COMPONENT für Feldzugriff in dynamischen Strukturen.
  • Casting mit CAST für Typkonvertierung von Deskriptoren.
  • Performance beachten – dynamischer Code ist langsamer.
  • Fehlerbehandlung bei nicht existierenden Typen/Feldern.
  • SQL Injection vermeiden bei dynamischen WHERE-Klauseln.
  • Dynamische Methodenaufrufe mit PARAMETER-TABLE.
  • Kombinieren Sie mit Field Symbols für effiziente Verarbeitung.
  • Verwenden Sie CDS Views für typsichere Definitionen.