This is a test message to test the length of the message box.
Login
BTP Table Entity
Erstellt von Software-Heroes

BTP - Table Entity

178

Die neuen ABAP Tabellen sind da. In diesem Artikel schauen wir uns die aktuellen Möglichkeiten an und welche Features noch nicht unterstützt werden.

Werbung


In diesem Artikel werden wir uns die Table Entities anschauen und wie sie später einmal die Tabellen im DDIC ablösen werden. Dabei gehen wir auf die verschiedenen Aspekte der Modellierung ein, aber auch der Verwendung. Der Abapeur hat sich bereits das Thema angeschaut, weiter Informationen findet ihr im Artikel von Clément.

 

Einleitung

Bereits auf dem Devtoberfest im letzten Jahr gab es einen Preview für die Table Entitäten im ABAP und wie sie die alten DDIC Tabellen ablösen werden. Mit dem Release 2502 (ABAP Environment oder Public Cloud) ist es nun auch so weit, die erste Version der Entitäten wurde ausgeliefert und kann von den Entwicklern getestet werden. Dazu haben wir uns ein neues kleines Datenmodell angelegt, welches wir in diesem Artikel verwenden möchten.

 

Wir möchten Startups verwalten und legen dazu eine Tabelle mit den verschiedenen Unternehmen und Ideen an. In jedem Startup arbeiten bereits ein paar Mitarbeiter, die die tollen Ideen entwickeln wollen. Dazu sammeln wir Geld von Investoren ein, die einen Anteil am Startup halten.

 

Anlage

In diesem Schritt werden wir mehrere Arten von Objekten anlegen, einmal die verschiedenen Typen die wir auch in unseren Entitäten verwenden werden, die eigentlichen Tabellen und etwas Logik um die Tabellen zu befüllen.

 

Typen

Grundsätzlich sollen die klassischen Typen wie Datenelement und Domänen in Zukunft durch die neuen Simple Types und Enums abgelöst werden. Daher legen wir einige Elemente für unsere Verwendung an. Wir benötigen einen Simple Type für unser Schlüsselfeld, welches wir in den verschiedenen Entitäten anlegen.

@EndUserText.label: 'StartUp UUID'
define type ZBS_ST_StartUpID: sysuuid_x16

 

Für das Ranking der neuen Unternehmen legen wir ein Enum an, da wir darüber eine feste Klassifizierung vornehmen können.

@EndUserText.label: 'StartUp Ranking'
define type ZBS_SE_Ranking : abap.char(1) enum
{
  @EndUserText.label: 'Unranked'
  Unranked = initial;
  @EndUserText.label: 'Premium'
  Premium  = 'S';
  @EndUserText.label: 'Very good'
  VeryGood = 'A';
  @EndUserText.label: 'Good'
  Good     = 'B';
  @EndUserText.label: 'No chance'
  NoChance = 'F';
}

 

Weitere Elemente legen wir dann für zusätzliche Felder und Eigenschaften an. Die vollständige Liste findest du im GitHub Repository unten verlinkt.

 

Tabellen

Legen wir also im ersten Schritt die Tabelle für das Startup an. Dabei definieren wir eine ROOT Tabelle, ähnlich wie bei RAP, bildet diese den Einstieg in unser Datenmodell. Dazu definieren wir auch zwei Compositions, die auf die unteren Kinder in der Hierarchie zeigen. Bereits auf dieser Ebene können wir weitere Annotationen für Texte und Überschriften definieren.

@AbapCatalog.deliveryClass: #APPLICATION_DATA
@AccessControl.authorizationCheck: #NOT_REQUIRED
@ClientHandling.type: #CLIENT_DEPENDENT
@EndUserText.label: 'Table for StartUp'
define root table entity ZBS_StartUp
{
  key StartUpID         : ZBS_ST_StartUpID;
      @EndUserText.label: 'Name of the StartUp'
      StartUpName       : abap.char(50);
      @EndUserText.label: 'Description'
      Description       : abap.char(200);
      @EndUserText.label: 'Register Number'
      CompanyRegisterID : abap.char(20);
      Rating            : ZBS_SE_Ranking;
      
      _Employee : composition of exact one to many ZBS_Employee;
      _Investor : composition of exact one to many ZBS_Investor;
}

 

Für die Table Entity gibt es zwei neue Annotationen im Header Bereich, die die Art der Tabelle definieren:

  • AbapCatalog.deliverClass - Beschreibt die Klassifizierung der Daten, wie Anwendungsdaten, Customizing, lokale Daten, etc.
  • ClientHandling - Wir können die Tabelle auf mandantenabhängig, -unabhängig oder auf Vererbung einstellen.

 

Schauen wir uns unsere Tabelle über "Rechts-Klick -> Show SQL CREATE Statement" an, dann finden wir weitere Details zu den Artefakten. Das CLIENT Feld wird auf der Datenbank automatisch mit generiert und muss in der Tabelle nicht angegeben werden. Es wird eine Tabelle mit dem Zusatz SAPABAP generiert und unsere Tabelle als Projektion auf der Datenbank, die von der Tabelle liest. Für das Enum wird ein Constraint definiert, damit nur die vorgesehenen Werte eingefügt werden können.

 

Nun definieren wir die Tabelle für die Mitarbeiter und definieren die Assoziationen zur Elterntabelle. Hier verwenden wir keine Root Tabelle mehr.

@AbapCatalog.deliveryClass: #APPLICATION_DATA
@AccessControl.authorizationCheck: #NOT_REQUIRED
@ClientHandling.type: #CLIENT_DEPENDENT
@EndUserText.label: 'Table for Employee'
define table entity ZBS_Employee
{
  key EmployeeID : ZBS_ST_EmployeeID;
      StartUpID  : ZBS_ST_StartUpID;
      @EndUserText.label: 'First name'
      FirstName  : abap.char(100);
      @EndUserText.label: 'Last name'
      LastName   : abap.char(100);
      @EndUserText.label: 'Salary'
      @Semantics.amount.currencyCode: 'Currency'
      Salary     : abap.curr(10,2);
      @EndUserText.label: 'Currency'
      Currency   : abap.cuky(5);
      @EndUserText.label: 'Manager'
      Manager    : ZBS_ST_EmployeeID;
      
      _StartUp : association to parent ZBS_StartUp on _StartUp.StartUpID = $projection.StartUpID;
}

 

Zum Abschluss legen wir noch die Investoren an und befüllen wie bei den anderen Tabellen die entsprechenden Felder, Annotationen und Beziehungen innerhalb der Daten.

@AbapCatalog.deliveryClass: #APPLICATION_DATA
@AccessControl.authorizationCheck: #NOT_REQUIRED
@ClientHandling.type: #CLIENT_DEPENDENT
@EndUserText.label: 'Table for Investor'
define table entity ZBS_Investor
{
  key InvestmentID  : sysuuid_x16;
      StartUpID     : ZBS_ST_StartUpID;
      @EndUserText.label: 'Name of the Investor'
      Name          : abap.char(100);
      @EndUserText.label: 'Invested sum'
      @Semantics.amount.currencyCode: 'Currency'
      InvestmentSum : abap.curr(15,2);
      @EndUserText.label: 'Currency'
      Currency      : abap.cuky(5);
      @EndUserText.label: 'Company shares'
      Shares        : abap.dec(5,2);
      
      _StartUp : association to parent ZBS_StartUp on _StartUp.StartUpID = $projection.StartUpID;
}

 

Wir haben nun unser Model, die Tabellen und Beziehungen definiert.

 

Befüllung

In diesem Schritt befüllen wir nun unsere Tabellen mit Daten. Dabei können wir unsere Tabellen wir klassische DDIC Tabellen verwenden und erst einmal als Data im Quellcode anlegen. Im nächsten Schritt erzeugen wir die Datensätze über VALUE Konstrukte.

DATA startups  TYPE STANDARD TABLE OF ZBS_StartUp WITH EMPTY KEY.
DATA employees TYPE STANDARD TABLE OF ZBS_Employee WITH EMPTY KEY.
DATA investors TYPE STANDARD TABLE OF ZBS_Investor WITH EMPTY KEY.

INSERT VALUE #( StartUpID         = xco_cp=>uuid( )->value
                StartUpName       = 'Unicorns'
                Description       = 'Innovative Ideas with Unicorns'
                CompanyRegisterID = 'UNI1245C5'
                Rating            = ZBS_SE_Ranking-premium )
       INTO TABLE startups REFERENCE INTO DATA(startup_uni).

INSERT VALUE #( StartUpID         = xco_cp=>uuid( )->value
                StartUpName       = 'Woodi'
                Description       = 'Hand-designed wooden furniture by the artist'
                CompanyRegisterID = 'WOO9749D1'
                Rating            = ZBS_SE_Ranking-good )
       INTO TABLE startups REFERENCE INTO DATA(startup_wood).

INSERT VALUE #( StartUpID         = xco_cp=>uuid( )->value
                StartUpName       = 'AI Runners'
                Description       = 'AI Tools for the ABAP Developer of Tomorrow'
                CompanyRegisterID = 'AIT6684F3'
                Rating            = ZBS_SE_Ranking-nochance )
       INTO TABLE startups REFERENCE INTO DATA(startup_ai).

employees = VALUE #( ( EmployeeID = 'C001'
                       StartUpID  = startup_uni->StartUpID
                       FirstName  = 'Skye'
                       LastName   = 'Robertson'
                       Salary     = '75000.00'
                       Currency   = 'EUR'
                       Manager    = '' )
                     ( EmployeeID = 'E001'
                       StartUpID  = startup_uni->StartUpID
                       FirstName  = 'Indiana'
                       LastName   = 'Fitz'
                       Salary     = '35200.00'
                       Currency   = 'EUR'
                       Manager    = 'C001' )
                     ( EmployeeID = 'E002'
                       StartUpID  = startup_uni->StartUpID
                       FirstName  = 'Kaua Correia'
                       LastName   = 'Rocha'
                       Salary     = '45750.00'
                       Currency   = 'EUR'
                       Manager    = 'C001' )
                     ( EmployeeID = 'C002'
                       StartUpID  = startup_wood->StartUpID
                       FirstName  = 'Peter'
                       LastName   = 'Wulf'
                       Salary     = '150000.00'
                       Currency   = 'USD'
                       Manager    = '' )
                     ( EmployeeID = 'E003'
                       StartUpID  = startup_wood->StartUpID
                       FirstName  = 'A''ishah Hafizah'
                       LastName   = 'Deeb'
                       Salary     = '55600.00'
                       Currency   = 'USD'
                       Manager    = 'C002' )
                     ( EmployeeID = 'C003'
                       StartUpID  = startup_ai->StartUpID
                       FirstName  = 'Kadeer Mutawalli'
                       LastName   = 'Hajjar'
                       Salary     = '36000.00'
                       Currency   = 'CHF'
                       Manager    = '' )
                     ( EmployeeID = 'C004'
                       StartUpID  = startup_ai->StartUpID
                       FirstName  = 'Kimberly Pérez'
                       LastName   = 'Bustos'
                       Salary     = '48000.00'
                       Currency   = 'CHF'
                       Manager    = 'C003' )
                     ( EmployeeID = 'E004'
                       StartUpID  = startup_wood->StartUpID
                       FirstName  = 'Ilmur'
                       LastName   = 'Sigþórsdóttir'
                       Salary     = '280000.00'
                       Currency   = 'CHF'
                       Manager    = 'C004' )
                     ( EmployeeID = 'E005'
                       StartUpID  = startup_wood->StartUpID
                       FirstName  = 'Xiao Chen'
                       LastName   = 'Hu'
                       Salary     = '235000.00'
                       Currency   = 'CHF'
                       Manager    = 'C004' )
                     ( EmployeeID = 'E006'
                       StartUpID  = startup_wood->StartUpID
                       FirstName  = 'Callum'
                       LastName   = 'Carroll'
                       Salary     = '278000.00'
                       Currency   = 'CHF'
                       Manager    = 'C004' ) ).

investors = VALUE #( ( InvestmentID  = xco_cp=>uuid( )->value
                       StartUpID     = startup_uni->StartUpID
                       Name          = 'Buffalo Shoes'
                       InvestmentSum = '300000.00'
                       Currency      = 'EUR'
                       Shares        = '10.50' )
                     ( InvestmentID  = xco_cp=>uuid( )->value
                       StartUpID     = startup_uni->StartUpID
                       Name          = 'Cuvox Unicorns'
                       InvestmentSum = '150000.00'
                       Currency      = 'EUR'
                       Shares        = '8.20' )
                     ( InvestmentID  = xco_cp=>uuid( )->value
                       StartUpID     = startup_uni->StartUpID
                       Name          = 'Skye Robertson'
                       InvestmentSum = '75000.00'
                       Currency      = 'EUR'
                       Shares        = '50.10' )
                     ( InvestmentID  = xco_cp=>uuid( )->value
                       StartUpID     = startup_wood->StartUpID
                       Name          = 'Walter L. Oliver'
                       InvestmentSum = '45000'
                       Currency      = 'USD'
                       Shares        = '20.00' ) ).

DELETE FROM ZBS_StartUp.
DELETE FROM ZBS_Employee.
DELETE FROM ZBS_Investor.

INSERT ZBS_StartUp FROM TABLE @startups.
INSERT ZBS_Employee FROM TABLE @employees.
INSERT ZBS_Investor FROM TABLE @investors.

 

Zum Abschluss leeren wir die Tabellen und übernehmen die neuen Datensätze per INSERT. Hier hat sich zur klassischen DDIC Tabelle nichts geändert.

 

Verwendung

In diesem Abschnitt gehen wir auf die Verwendung der Tabellen ein und ob es Unterschiede zu den DDIC Tabellen bzw. Core Data Services gibt.

 

SELECT

Beginnen wir mit einem einfachen SELECT und lesen über eine EmployeeID uns einen Mitarbeiter ein. Funktioniert so weit wie immer bei Tabellen und Core Data Services.

SELECT SINGLE FROM ZBS_Employee
  FIELDS *
  WHERE EmployeeID = @employee_id
  INTO @result.

 

Im nächsten Schritt möchten wir Daten über die Assoziationen lesen, ein Feature, das in Core Data Services weit verbreitet ist.

SELECT FROM ZBS_StartUp AS Start
  FIELDS Start~StartUpName,
         concat_with_space( \_Employee-FirstName, \_Employee-LastName, 1 ) as FullName,
         \_Investor-Name
  WHERE Rating = @ZBS_SE_Ranking-premium
  INTO TABLE @DATA(mixed_data).

 

Allerdings erhalten wir hier eine Fehlermeldung vom Compiler: "The target "ZBS_EMPLOYEE" of the association "_EMPLOYEE" cannot be used in ABAP SQL." und wir können die Abfrage nicht aktivieren.  Wir können allerdings einen Workaround über die Joins machen, was aber dem Gedanken der Wiederverwendbarkeit widersprechen würde.

SELECT
  FROM ZBS_StartUp AS Start
         LEFT OUTER JOIN
           ZBS_Employee AS Employee ON Employee~StartUpID = Start~StartUpID
             LEFT OUTER JOIN
               ZBS_Investor AS Investor ON Investor~StartUpID = Start~StartUpID
  FIELDS Start~StartUpName,
         concat_with_space( Employee~FirstName, Employee~LastName, 1 ) AS FullName,
         Investor~Name
  WHERE Rating = @ZBS_SE_Ranking-premium
  INTO TABLE @DATA(mixed_data).

 

Ein entsprechendes Ergebnis würden wir dann erhalten:

 

CUD

Die Änderungsoperationen (Create, Update, Delete) haben wir bereits bei der Anlage der Objekte verwendet, in dem wir Einträge gelöscht haben und neue Einträge generiert haben. Hier verhalten sich Table Entities wie normale Tabellen und können auch so verwendet werden. Im Abschnitt der Befüllung findest du weitere Beispiele.

 

Data Preview

Der Data Preview (Kontextmenü oder im Objekt per F8) funktioniert auch für die Table Entity, wir bekommen die Daten angezeigt. Die Enums werden nicht mit den technischen Werten dargestellt, sondern mit den Konstanten, was es in der Tabelle leicht lesbar macht.

 

Möchten wir allerdings über die angelegte Assoziation zu den Daten navigieren, dann funktioniert das über diese Art leider nicht. Dies entspricht der aktuellen Fehlermeldung beim SELECT.

 

ABAP Unit

Die spannende Frage wäre nun im ABAP Unit Bereich, welchen Double verwenden wir für die Table Entity? An sich ist es ein CDS Artefakt, aber auch die neue Art von Tabelle. Dazu implementieren wir eine Testklasse und versuchen beide Doubles für das neue Artefakt zu erstellen.

CLASS ltc_table_entity DEFINITION FINAL
  FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.

  PRIVATE SECTION.
    CLASS-DATA sql_environment TYPE REF TO if_osql_test_environment.

    CLASS-METHODS class_setup.
    CLASS-METHODS class_teardown.

    METHODS check_tdf          FOR TESTING.
    METHODS check_not_original FOR TESTING.
ENDCLASS.

CLASS zcl_bs_demo_table_entity_crud DEFINITION LOCAL FRIENDS ltc_table_entity.

CLASS ltc_table_entity IMPLEMENTATION.
  METHOD class_setup.
    DATA double_data TYPE STANDARD TABLE OF ZBS_Employee WITH EMPTY KEY.

    sql_environment = cl_osql_test_environment=>create( VALUE #( ( 'ZBS_EMPLOYEE' ) ) ).

    double_data = VALUE #( Currency = 'USD'
                           ( EmployeeID = 'C001'
                             FirstName  = 'Brian'
                             LastName   = 'Jonsson'
                             Salary     = '50000.00'
                             Manager    = '' )
                           ( EmployeeID = 'E001'
                             FirstName  = 'Marlene'
                             LastName   = 'Geralt'
                             Salary     = '45200.00'
                             Manager    = 'C001' ) ).

    sql_environment->insert_test_data( double_data ).
  ENDMETHOD.


  METHOD class_teardown.
    sql_environment->destroy( ).
  ENDMETHOD.


  METHOD check_tdf.
    DATA(cut) = NEW zcl_bs_demo_table_entity_crud( ).

    DATA(result) = cut->select_employee( 'C001' ).

    cl_abap_unit_assert=>assert_equals( exp = 'Brian'
                                        act = result-FirstName ).
  ENDMETHOD.


  METHOD check_not_original.
    DATA(cut) = NEW zcl_bs_demo_table_entity_crud( ).

    DATA(result) = cut->select_employee( 'E004' ).

    cl_abap_unit_assert=>assert_differs( exp = 'Ilmur'
                                         act = result-FirstName ).
  ENDMETHOD.
ENDCLASS.

 

Aktuell wird das Objekt als STTE klassifiziert, doch keines der beiden Test Double Frameworks unterstützt aktuell diesen Typen. Damit können wir keine Testumgebung dafür erzeugen.

 

Bewertung

Wie sieht es also mit dem aktuellen Einsatz der Table Entity im Alltag aus? In diesem Kapitel schauen wir uns an, was bereits sehr gut funktioniert und Vorteile bei der Nutzung hat, aber auch Dinge, die noch nicht so gut funktionieren oder scheinbar noch fehlerhaft sind.

 

Vorteile

Welche Vorteile und Boni bekommen wir mit der aktuellen Version, wenn wir unsere DDIC Tabellen durch die Table Entitäten austauschen?

  • Modellierung - Der Name der Tabelle kann nun 32 Zeichen lang sein und mit CamelCase geschrieben werden. Ebenso die Felder können bereits wie in Core Data Services modelliert und benannt werden.
  • Beziehungen - Die Beziehungen der Daten kann bereits auf der untersten Ebene definiert und zur Verfügung gestellt werden. Damit sind alle wichtigen Bestandteile bereits zur Modellierungszeit verfügbar.
  • Berechtigung - Bereits auf unterster Ebene können die Daten mit einer Access Control gesichert werden, damit muss im Code keine Berechtigungsprüfung mehr implementiert werden.
  • CDB - Der Custom Data Browser unterstützt bereits die Table Entitäten, dabei werden aber die Annotationen für Texte noch nicht unterstützt und angezeigt. Hier müsstest du noch Datenelemente verwenden.

 

Gaps

In diesem Abschnitt ein paar Fehler und Gaps die uns bei der Entwicklung aufgefallen sind oder die uns Kollegen mitgegeben haben:

  • RAP Unterstützung - Aktuell gibt es keine Unterstützung für die direkte Einbindung von Tabellen in RAP Objekte. Grundsätzlich kannst du eine Table Entity einbinden, es gibt allerdings Probleme dann beim Draft-Handling und Enums werden nicht unterstützt.
  • Int1 - Aktuell unterstützen die Tabellen keine INT1 Felder, hier haben wir keinen genauen Grund in der Dokumentation gefunden, wieso der Basis Typ nicht unterstützt wird.
  • Berechtigung - Ist ein Access Control an einer Table Entity vorhanden und wir ändern die Table Entity und aktivieren diese, können wir alle Daten im Data Preview anschauen. Erst nach der erneuter Aktivierung der Berechtigung, greift diese erneut.
  • Wertehilfe - An einigen Stellen können wir Tabellen als Wertehilfen hinterlegen, wie zum Beispiel bei Berechtigungsobjekten. Hier werden die Objekte nicht erkannt und unterstützt.
  • Dokumentation - Aktuell ist es schwer eine passende Dokumentation zu finden und alle Bestandteile und Verwendungen sauber erklärt zu bekommen. Hier wird SAP sehr wahrscheinlich die nächsten Wochen nachliefern.
  • ABAP Unit - Aktuell wird die Table Entity nicht im ABAP Unit in Bezug auf das TDF unterstützt.
  • Data Preview - Der Data Preview in Eclipse funktioniert aktuell wie gewünscht, allerdings nicht die Navigation über die angelegten Beziehungen.
  • Includes - Aktuell gibt es keine Möglichkeiten Includes anzulegen und in Tabellen zu verwenden. Diese werden normalerweise für die Wiederverwendbarkeit benötigt oder um beim Draft das Admin-Include zu übernehmen.

 

Vollständiges Beispiel

Du möchtest das Beispiel bei dir auf dem System ausprobieren? Hier findest du unser GitHub Repository, was die verschiedenen Artefakte enthält, die wir hier im Artikel gezeigt haben.

 

Fazit

Die Table Entity hat ihren ersten Start in der Community vollzogen und ist unserer Meinung nach einer guter Schritt zu einem Re-Design der Tabellen. In den nächsten Releases muss die RAP Integration dann zeigen, was in ihr steckt und ob Enums nativ unterstützt werden, auch mit Wertehilfe. Bis dahin sollten auch die Fehler aus den Standardtools behoben sein.

 

Quelle:
SAP Community - ABAP News for SAP BTP ABAP Environment 2502
SAP Help - Table Entities


Enthaltene Themen:
BTPABAP EnvironmentTable Entity
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


RAP - Tree View (Löschverhalten)

Kategorie - ABAP

In diesem Artikel schauen wir uns das Verhalten beim Löschen von Knoten im Tree View mit RAP an. Dabei gibt es einige interessante Punkte zu beachten.

15.04.2025

BTP - User Anlage per API

Kategorie - ABAP

Wie kannst du eigentlich den Employee und den Business User per API im ABAP Environment anlegen? In diesem Artikel gehen wir auf die Details und die Implementierung ein.

11.04.2025

RAP - Tree View

Kategorie - ABAP

Du möchtest eine Hierarchie in RAP ganz einfach darstellen? Wie das in ABAP Cloud funktioniert, erfährst du in diesem Artikel.

08.04.2025

RAP - Classic Pattern

Kategorie - ABAP

In diesem Artikel schauen wir uns das Classic Pattern an und gehen auf die Anwendungsfälle der Implementierung in ABAP Cloud ein.

25.03.2025

BTP - Schnittstellen Performance

Kategorie - ABAP

Welche Schnittstellentechnologie hat aktuell die beste Performance beim Zugriff auf On-Premise Daten in ABAP? Hier gehen wir mehr in die Details.

07.03.2025