
RAP - EML (Teil 2)
Komplexe RAP Entitäten mit der Entity Manipulation Language ansteuern, darum geht es in diesem Artikel und wie du mit tiefen Knoten arbeitest.
Inhaltsverzeichnis
Die Grundlagen für die Entity Manipulation Language, kurz EML, haben wir bereits vor einer Weile in einem ersten Artikel gelegt. Dort haben wir dir gezeigt, was EML ist und wie du es mit RAP Objekten einsetzen kannst. In diesem Artikel schauen wir uns das Gleiche bei komplexen Entitäten an.
Einleitung
Im Artikel letzte Woche haben wir gemeinsam ein komplexes Datenmodell aufgebaut und uns die Unterschiede zu einer einfachen Entität angeschaut. Dabei besteht das Modell nicht nur aus einer einzigen Tabelle, sondern mehrere Tabellen bilden ein Objekt ab, so wie es in der "normalen" Welt der Standard ist.
EML bildet die Grundlage zum Zugriff auf RAP Objekte, ohne über den OData Service zu gehen, sondern als schneller Weg im aktuellen System auf Funktionen des Objektes zuzugreifen. Die Funktionen sind innerhalb eines Objekts stark gekapselt und damit leicht wartbar und wiederverwendbar.
Bei der Entwicklung der Statements unterstützt ebenso die IDE (Eclipse) und gibt passende Vorschläge beim Zugriff auf die Entitäten:
Lesen
Im ersten Schritt lesen wir mit einem EML Statement beide abhängige Entitäten, dazu definieren wir uns einen Filter, dieser kann später von außen kommen oder auf anderen Wege aufgebaut werden. Dazu selektieren wir zwei Belege von der Datenbank, die Belegnummern können dabei zu deinen Belegen abweichen:
DATA lt_filter TYPE STANDARD TABLE OF ZBS_R_RAPCInvoice WITH EMPTY KEY.
lt_filter = VALUE #( ( Document = '30000000' )
( Document = '30000005' ) ).
Nun bauen wir das entsprechende Lesestatement auf, zur besseren Lesbarkeit haben wir es einmal aufbereitet geschrieben, damit wir die einzelnen Schritte beschreiben können:
READ ENTITIES OF ZBS_R_RAPCInvoice
ENTITY Invoice
ALL FIELDS WITH CORRESPONDING #( lt_filter )
RESULT DATA(lt_invoice)
ENTITY Invoice BY \_Position
FIELDS ( Document PositionNumber Material ) WITH CORRESPONDING #( lt_filter )
RESULT DATA(lt_position)
FAILED FINAL(ls_failed).
Zuerst einmal sprechen wir das RAP Objekt an, aus welchem wir die Informationen lesen wollen. Dann geben wir die erste Entität an, aus der wir die Daten lesen wollen. Das Schema entspricht dem Standard, nach der Entität kommt die Einschränkung der Felder, dann der Schlüssel und schließlich weisen wir das Ergebnis einer Variable zu. Im zweiten Abschnitt wiederholen wir dieses Verhalten, zur besseren Veranschaulichung haben wir dieses Mal die Felder eingegrenzt, um nicht alle Inhalte von der Datenbank zu lesen. Den Filter übergeben wir wieder und können das Ergebnis zuweisen. Zum Schluss übernehmen wir noch die fehlgeschlagenen Zugriffe. In der Struktur sind Informationen enthalten, wenn bestimmte Zugriffe nicht funktioniert haben.
Das Ergebnis aus den Tabellen sieht nun wie folgt aus:
Einfügen
Im zweiten Schritt wollen wir einmal neue Daten einfügen und in einem Schwung Rechnungen und die passenden Positionen anlegen. Dazu bereiten wir die Daten vor, die wir im nächsten Schritt einfügen wollen:
DATA lt_new_invoice TYPE TABLE FOR CREATE ZBS_R_RAPCInvoice.
DATA lt_new_position TYPE TABLE FOR CREATE ZBS_R_RAPCInvoice\_Position.
lt_new_invoice = VALUE #( ( %cid = 'B1'
Document = '40000000'
Partner = '1000000004'
%control = VALUE #( Document = if_abap_behv=>mk-on Partner = if_abap_behv=>mk-on ) ) ).
lt_new_position = VALUE #(
( %cid_ref = 'B1'
%target = VALUE #(
( %cid = 'P1'
PositionNumber = 1
Material = 'R0001'
%control = VALUE #( PositionNumber = if_abap_behv=>mk-on Material = if_abap_behv=>mk-on ) )
( %cid = 'P2'
PositionNumber = 2
Price = '2.20'
Currency = 'EUR'
%control = VALUE #( PositionNumber = if_abap_behv=>mk-on Price = if_abap_behv=>mk-on Currency = if_abap_behv=>mk-on ) ) ) ) ).
Zu beachten beim Datenaufbau ist die Verwendung der richtigen Referenz (%CID), denn damit wir die Rechnung mit der Position verbunden. Unter %TARGET hängen wir dann unsere Daten an die Position dran. Die Positionen erhalten dazu eigene IDs. Wichtig beim Vorbereiten der Daten ist die Befüllung der %CONTROL Struktur, da nur dadurch die Felder beim Einfügen auch befüllt werden. Eine Besonderheit gibt es noch beim Erstellen der Datentypen für die Hauptentität und die passende abhängige Entität. Der Aufruf der Anlage sieht dann wieder wie folgt aus:
MODIFY ENTITIES OF ZBS_R_RAPCInvoice
ENTITY Invoice
CREATE FROM lt_new_invoice
ENTITY Invoice
CREATE BY \_Position FROM lt_new_position
FAILED DATA(ls_failed).
COMMIT ENTITIES.
Aufruf des MODIFY um die ändernde Aktion einzuleiten. Dann wird die erste Entität mitgegeben (Rechnungen) und im nächsten Schritt die Positionen, hier aber darauf achten, dass die Anlage über die Assoziation stattfindet. Es ist ebenso möglich direkt Positionen anzulegen, dafür müssten aber die Schlüssel (Dokumentennummer) bekannt sein. Im letzten Schritt nehmen wir noch die Struktur entgegen, um auf Fehler reagieren zu können. Im Anschluss nicht vergessen einen COMMIT zu setzen, damit die Daten auf die Datenbank geschrieben werden.
Über den Data-Preview können wir uns die neuen Datensätze anschauen, hier einmal ein Auszug aus der Positionstabelle mit den beiden erstellten Positionen der Rechnung:
Löschen
Bevor wir den Datensatz löschen können, sollten wir zuerst einmal definieren, welche Schlüssel wir löschen wollen. Dazu verwenden wir wieder eine Dummy Tabelle als Filter der Datensätze.
DATA lt_filter TYPE STANDARD TABLE OF ZBS_R_RAPCInvoice WITH EMPTY KEY.
lt_filter = VALUE #( ( Document = '40000000' ) ).
Im nächsten Schritt geben wir die Schlüssel dem MODIFY mit. Hier reicht es über das Dokument zu löschen, es werden alle abhängigen Entitäten berücksichtigt und ebenfalls von der Datenbank gelöscht.
MODIFY ENTITIES OF ZBS_R_RAPCInvoice
ENTITY Invoice
DELETE FROM CORRESPONDING #( lt_filter )
FAILED DATA(ls_failed).
COMMIT ENTITIES.
Das Statement zum Löschen ist sehr kurz und doch sehr praktisch. RAP kümmert sich um die abhängigen Entitäten die weiter unten kommen, sodass wir beim Löschen nicht darauf achten müssen.
Vollständiges Beispiel
Am Ende noch einmal das vollständige Beispiel der gezeigten Demos. Für die Formatierung verwenden wir den ABAP Cleaner, ein praktisches Werkzeug um Quellcode sauber zu halten:
CLASS zcl_bs_demo_crap_eml DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PRIVATE SECTION.
METHODS read_data
IMPORTING io_out TYPE REF TO if_oo_adt_classrun_out.
METHODS insert_data
IMPORTING io_out TYPE REF TO if_oo_adt_classrun_out.
METHODS delete_data
IMPORTING io_out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.
CLASS zcl_bs_demo_crap_eml IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
read_data( out ).
insert_data( out ).
delete_data( out ).
ENDMETHOD.
METHOD read_data.
DATA lt_filter TYPE STANDARD TABLE OF ZBS_R_RAPCInvoice WITH EMPTY KEY.
lt_filter = VALUE #( ( Document = '30000000' )
( Document = '30000005' ) ).
READ ENTITIES OF ZBS_R_RAPCInvoice
ENTITY Invoice
ALL FIELDS WITH CORRESPONDING #( lt_filter )
RESULT DATA(lt_invoice)
ENTITY Invoice BY \_Position
FIELDS ( Document PositionNumber Material ) WITH CORRESPONDING #( lt_filter )
RESULT DATA(lt_position)
FAILED FINAL(ls_failed).
IF ls_failed-invoice IS NOT INITIAL.
io_out->write( `Failed!` ).
ENDIF.
io_out->write( `Invoices:` ).
io_out->write( lt_invoice ).
io_out->write( `Positions:` ).
io_out->write( lt_position ).
ENDMETHOD.
METHOD insert_data.
DATA lt_new_invoice TYPE TABLE FOR CREATE ZBS_R_RAPCInvoice.
DATA lt_new_position TYPE TABLE FOR CREATE ZBS_R_RAPCInvoice\_Position.
lt_new_invoice = VALUE #( ( %cid = 'B1'
Document = '40000000'
Partner = '1000000004'
%control = VALUE #( Document = if_abap_behv=>mk-on Partner = if_abap_behv=>mk-on ) ) ).
lt_new_position = VALUE #(
( %cid_ref = 'B1'
%target = VALUE #(
( %cid = 'P1'
PositionNumber = 1
Material = 'R0001'
%control = VALUE #( PositionNumber = if_abap_behv=>mk-on Material = if_abap_behv=>mk-on ) )
( %cid = 'P2'
PositionNumber = 2
Price = '2.20'
Currency = 'EUR'
%control = VALUE #( PositionNumber = if_abap_behv=>mk-on Price = if_abap_behv=>mk-on Currency = if_abap_behv=>mk-on ) ) ) ) ).
MODIFY ENTITIES OF ZBS_R_RAPCInvoice
ENTITY Invoice
CREATE FROM lt_new_invoice
ENTITY Invoice
CREATE BY \_Position FROM lt_new_position
FAILED DATA(ls_failed).
COMMIT ENTITIES.
IF ls_failed-invoice IS NOT INITIAL.
io_out->write( `Failed!` ).
ELSE.
io_out->write( `Creation OK` ).
ENDIF.
ENDMETHOD.
METHOD delete_data.
DATA lt_filter TYPE STANDARD TABLE OF ZBS_R_RAPCInvoice WITH EMPTY KEY.
lt_filter = VALUE #( ( Document = '40000000' ) ).
MODIFY ENTITIES OF ZBS_R_RAPCInvoice
ENTITY Invoice
DELETE FROM CORRESPONDING #( lt_filter )
FAILED DATA(ls_failed).
COMMIT ENTITIES.
IF ls_failed-invoice IS NOT INITIAL.
io_out->write( `Failed!` ).
ELSE.
io_out->write( `Deletion OK` ).
ENDIF.
ENDMETHOD.
ENDCLASS.
Fazit
Das Arbeiten mit EML sollte dir nach unserem Artikel einfacher fallen und auch bei komplexen und größeren RAP Objekten musst du keine Angst haben, diese nicht mehr handeln zu können. Es gibt einige Dinge zu beachten, wenn du mit mehreren Entitäten arbeitest.