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

BTP - Table Entity

1350

The new ABAP tables are here. In this article we look at the current options and which features are not yet supported.



In this article we will look at table entities and how they will later replace tables in the DDIC. We will look at the different aspects of modeling, but also their use. The Abapeur has already looked at the topic, you can find further information in the article by Clément.

 

Introduction

Already at the Devtoberfest last year there was a preview of the table entities in ABAP and how they will replace the old DDIC tables. With release 2502 (ABAP Environment or Public Cloud) the time has now come; the first version of the entities has been delivered and can be tested by the developers. For this purpose we have created a new small data model which we would like to use in this article.

 

We would like to manage startups and are creating a table with the various companies and ideas. Each startup already has a few employees who want to develop the great ideas. To do this, we are raising money from investors who hold a share in the startup.

 

Creation

In this step, we will create several types of objects, the different types that we will also use in our entities, the actual tables, and some logic to fill the tables.

 

Types

Basically, the classic types such as data elements and domains will be replaced in the future by the new Simple Types and Enums. We will therefore create some elements for our use. We need a simple type for our key field, which we create in the various entities.

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

 

We create an enum to rank the new companies, as this allows us to make a fixed classification.

@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';
}

 

We then create further elements for additional fields and properties. You can find the complete list in the GitHub repository linked below.

 

Tables

So in the first step we create the table for the startup. We define a ROOT table, similar to RAP, this forms the entry point into our data model. We also define two compositions that point to the lower children in the hierarchy. We can already define further annotations for texts and headings at this level.

@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;
}

 

For the Table Entity there are two new annotations in the header area that define the type of table:

  • AbapCatalog.deliverClass - Describes the classification of the data, such as application data, customizing, local data, etc.
  • ClientHandling - We can set the table to be client-dependent, client-independent or inheritance.

 

If we look at our table via "Right-Click -> Show SQL CREATE Statement", we will find further details about the artifacts. The CLIENT field is automatically generated in the database and does not have to be specified in the table. A table with the addition SAPABAP is generated and our table as a projection on the database that reads from the table. A constraint is defined for the enum so that only the intended values can be inserted.

 

Now we define the table for the employees and define the associations to the parent table. Here we no longer use a root table.

@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;
}

 

Finally, we create the investors and, as with the other tables, fill in the corresponding fields, annotations and relationships within the data.

@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;
}

 

We have now defined our model, the tables and relationships.

 

Filling

In this step, we now fill our tables with data. We can use our tables like classic DDIC tables and first create them as data in the source code. In the next step, we create the data sets using VALUE constructs.

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.

 

Finally, we empty the tables and import the new data records via INSERT. Nothing has changed here compared to the classic DDIC table.

 

Usage

In this section, we will discuss the use of the tables and whether there are any differences to the DDIC tables or Core Data Services.

 

SELECT

Let's start with a simple SELECT and read in an employee using an EmployeeID. Works as always with tables and Core Data Services.

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

 

In the next step, we want to read data about the associations, a feature that is widely used in Core Data Services.

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).

 

However, we get an error message from the compiler: "The target "ZBS_EMPLOYEE" of the association "_EMPLOYEE" cannot be used in ABAP SQL." and we cannot activate the query.  We can, however, create a workaround using joins, but this would contradict the idea of reusability.

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).

 

We would then receive a corresponding result:

 

CUD

We have already used the change operations (Create, Update, Delete) when creating the objects by deleting entries and generating new entries. Here, table entities behave like normal tables and can be used in this way. You can find further examples in the filling section.

 

Data Preview

The data preview (context menu or in the object using F8) also works for the table entity; we get the data displayed. The enums are not displayed with the technical values, but with the constants, which makes it easy to read in the table.

 

However, if we want to navigate to the data via the created association, then unfortunately this does not work in this way. This corresponds to the current error message when SELECT is used.

 

ABAP Unit

The exciting question now in the ABAP Unit area would be which double do we use for the table entity? In itself it is a CDS artifact, but also the new type of table. To do this, we implement a test class and try to create both doubles for the new artifact.

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.

 

The object is currently classified as STTE, but neither of the two test double frameworks currently supports this type. This means we cannot create a test environment for it.

 

Evaluation

So what is the current use of the table entity in everyday life? In this chapter we look at what already works very well and has advantages when used, but also things that do not work so well or still seem to be faulty.

 

Advantages

What advantages and bonuses do we get with the current version if we replace our DDIC tables with the table entities?

  • Modeling - The name of the table can now be 32 characters long and written in CamelCase. The fields can also be modeled and named like in Core Data Services.
  • Relationships - The relationships of the data can already be defined and made available at the lowest level. This means that all important components are already available at modeling time.
  • Authorization - The data can be secured with an access control even at the lowest level, so that no authorization check needs to be implemented in the code.
  • CDB - The Custom Data Browser already supports table entities, but the annotations for texts are not yet supported and displayed. You would have to use data elements here.

 

Gaps

In this section there are a few errors and gaps that we noticed during development or that colleagues told us about:

  • RAP support - There is currently no support for the direct integration of tables into RAP objects. In principle, you can integrate a table entity, but there are problems with draft handling and enums are not supported.
  • Int1 - The tables currently do not support INT1 fields. We have not found an exact reason in the documentation as to why the base type is not supported.
  • Authorization - If an access control is present on a table entity and we change the table entity and activate it, we can view all the data in the data preview. It only takes effect again after the authorization is activated again.
  • Value help - In some places we can store tables as value help, for example with authorization objects. The objects are not recognized and supported here.
  • Documentation - It is currently difficult to find suitable documentation and to get all components and uses clearly explained. SAP will most likely provide this in the next few weeks.
  • ABAP Unit - The table entity is currently not supported in the ABAP Unit with regard to the TDF.
  • Data Preview - The data preview in Eclipse currently works as desired, but not the navigation via the created assoziations.
  • Includes - Currently there are no options to create and use includes in tables. These are usually needed for reusability or to adopt the admin include for draft.

 

Complete example

Would you like to try out the example on your system? Here you can find our GitHub repository, which contains the various artifacts that we have shown in this article.

 

Conclusion

The Table Entity has had its first launch in the community and is, in our opinion, a good step towards a re-design of the tables. In the next releases, the RAP integration must then show what it is capable of and whether enums are natively supported, also with value help. By then, the errors from the standard tools should also be fixed.

 

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


Included topics:
BTPABAP EnvironmentTable Entity
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


BTP - Interface Performance

Category - ABAP

Which interface technology currently has the best performance when accessing On-Premise data in ABAP? Here we go into more detail.

03/07/2025

BTP - Basic Authentication & Principal Propagation

Category - ABAP

In this article we look at the login options from the ABAP environment to on-premise and how they influence your development.

03/04/2025

BTP - HTTP Service

Category - ABAP

Do you need a generic endpoint in the ABAP environment? In this article we look at the possibility of HTTP services.

02/14/2025

BTP - ABAP Unit Runner

Category - ABAP

How can you regularly run your ABAP unit tests on the ABAP environment and have the results sent to you? There is currently no standard for this.

02/11/2025

BTP - Custom Data Browser

Category - ABAP

What alternatives to SE16 do you have in the ABAP environment to give your department easy access to data? In this article, we'll go into the details.

01/31/2025