Defining Composite Business Objects

Objective

After completing this lesson, you will be able to define compositions.

Composite Business Objects

CDS views for composite RAP business object

Up to now, we worked with RAP Business Objects that consisted of one single node, the root entity. More generally, a business object (BO) consists of a hierarchical tree of nodes where each node is an element that is modeled with a CDS entity and arranged along a composition path.

At runtime, one instance of the BO consists of exactly one instance of the root entity and a variable number of instances of the child entities. A sales order, for example, consists of exactly one header (the root entity instance) and several items (child entity instances).

The hierarchy of entities is defined through two special kinds of associations, namely by Compositions and To-Parent Associations.

Note

It is not necessary to define foreign key relations between the underlying tables. They are included in the picture to illustrate that the associations in the CDS data model correspond to relations in the relational data model on ABAP Dictionary level.

The composition tree

In RAP, a composition is a specialized association that defines a whole-part relationship and always leads from the parent to the direct child. A child entity (composite part) only exists together with its parent entity (whole). The definition of a composition always requires the definition of a corresponding to-parent association that leads from the child entity to the direct parent entity.

In the example, the hierarchy consists of three entities, the root entity (Text), its direct child entity (Line), and an indirect child entity (Word), which has the first child entity as its parent. The text is a composition of text lines and each line is a composition of words. For each of the two compositions, there is a corresponding to-parent association.

And the compositions and to-parent associations, it is possible to define other relations within the composition tree, for example, from a child entity's child to the root entity. Such relations are defined with ordinary associations. They are not mandatory in general, but can be needed in certain circumstances, for example, to establish a lock master/ lock dependent relation over more than two layers.

The following restrictions apply when modeling the composition tree of a RAP Business Objects:

  • There is exactly one root entity.
  • The root entity may be the source but must not be the target of a composition.
  • Source and target of a composition are never the same entity.

The cardinality of an association expresses how many instances of an entity can be involved in the relationship. It specifies the number of entity instances that are connected to a single source instance and is expressed with a lower bound and an upper bound in the form: (lower_bound..upper_bound). In a RAP BO, the cardinality of the composition can be 0..1 or 0..n, but the cardinality of a to-parent association always has to be 1..1.

CDS Compositions and To-Parent Associations

Example of root entity

The root entity is of particular importance in a composition tree. The root entity serves as a representation of the business object and defines the top node within a hierarchy in a business object's structure. This is considered in the source code of the CDS data definition for /LRN/R_Text with the keyword ROOT.

The root entity (/LRN/R_Text) serves as the source of a composition which is defined using the keyword COMPOSITION in the corresponding data definition. The target of this composition (/LRN/R_Line) defines the direct child entity.

CDS compositions are defined similarly to CDS associations. The same rules apply for the cardinality and the name of the composition. The main difference is that for a composition no ON-condition is defined explicitly. The ON condition is generated automatically using the ON condition of the to-parent association of the composition target.

The name of the composition must be added exactly once to the select_list of the CDS view entity it is defined in, without attributes and alias. If no name is defined for the composition, the name of the composition is the name of the target entity target, and this name must be made available in the SELECT list.

Caution

Fields from a composition target can't be used locally in the SELECT list, WHERE clause, or any other position of the view entity in which it is defined.
An example of direct child entity

If a CDS view entity is the target of a composition, it has to define a CDS to-parent association. The to-parent association is defined using the special syntax ASSOCIATION TO PARENT.

The direct child entity (/LRN/R_Line) servers as target of a composition and therefore defines a to-parent association to the direct parent entity (/LRN/R_Text).

CDS to-parent associations are defined similarly to CDS associations. The same rules apply for the name of the association. An ON condition has to be defined for which some certain rules apply.

The main difference is that for a to-parent association the cardinality cannot be defined explicitly for to-parent associations and is generated as [1..1].

A child entity cannot have more than one to-parent associations but itself be a parent entity and define further compositions. Child entity /LRN/R_Line, for example, is the parent of child entity /LRN/R_Word.

The name of the to-parent association must be added exactly once to the select list of the CDS view entity it is defined in, without attributes and alias. If no name is defined for the composition, the name of the composition is the name of the target entity target, and this name must be made available in the SELECT list.

The following rules apply to the operands and syntax of the ON condition of a to-parent association:

  • Only key fields of the parent entity
  • All key fields of the parent entity
  • Each field of child entity only once
  • Fields on child entity with prefix $projection
  • Only comparison with "="
  • No OR or NOT

Hint

To avoid syntax errors, it is recommended to define the to-parent association first and the corresponding composition after.
An example child entity of a child entity

The child entity of a child entity only requires the to-parent association to its direct parent. It is not mandatory to define a direct association to the root entity and there is no special association type for that purpose. The child entity /LRN/R_Word of /LRN/R_Line does not necessarily require an association to the root entity (/LRN/R_Text).

We see later that, when adding the behavior for the RAP BO, such an association can be helpful. For example, to reference the root entity as lock master or authorization master.

If an association to the root entity is needed, it is defined as an ordinary association with cardinality (1..1).

Composition in the Behavior Definition

Code snippet with behavior definition highlighted

While each entity of a composite business object has its own data definition, there is only one behavior definition source per business object.

After some general properties of the business object, for example, the implementation type of draft/non draft enabled implementation, the behavior definition source contains exactly one define behavior statement for each entity of the hierarchy.

Code snippet with behavior definition highlighted and respective behavior pool

If a behavior definition source contains more than one DEFINE BEHAVIOR FOR statements, the corresponding behavior pool, that is the global ABAP class specified after IMPLEMENTATION IN CLASS, contains one local handler class for each of the entities.

We recommend that the name of the local handler class is lhc_<entity_name> where <entity_name> is the name of the CDS view entity or, if provided, the alias name for the entity from the behavior definition.

Hint

When you use the available quick fix to generate the local handler classes, the name automatically follows this guideline.
An example of Associations in the Behavior Definition

By adding your associations to the behavior definition, you explicitly enable read access and create access for your associations. This means that you allow a RAP BO consumer to read data from related entity instances or to create new instances of the association target entity.

Read and create access is defined with the association _Assoc { create; } statement. Create access is only allowed for compositions. It is not allowed for to-parent associations. This means that child nodes can be created via their parent node, but parents can't be created via their child nodes.

Read access only is defined with association _Assoc; or with the alternative syntax variant, association _Assoc { }. This is allowed for any association defined in the CDS view entity.

Note

Even without explicit declaration, to-parent associations are automatically read enabled and compositions are automatically read and create enabled. We still recommend to specifying the read- and create-by-association operations explicitly. In strict syntax mode, this is enforced by the syntax check.
Variants of the Association Statement

Several operation additions are available to restrict the usage of an association. If internal is placed before keyword association, read and create access are forbidden for an outside consumer of the business object. If it is placed within the curly brackets, before the keyword create, the create access is restricted, but read access is available for outside consumers.

As for the standard operations, update, and delete, you can implement instance feature control for the create operation. To do so, add ( features : instance ) within the curly brackets, after keyword create.

An example of Draft Enabled Associations

By adding with draft; inside the curly brackets, you specify that the association is draft-enabled. A draft-enabled association retrieves active data if it is followed from an active instance and draft data if it is followed from a draft source instance.

If a business object is draft-enabled, then all associations must be draft-enabled, so that the associations always lead to the target instance with the same state (draft or active).

Note

When you draft-enable a BO by adding with draft, all BO-internal associations are automatically draft-enabled. To make this behavior explicit, the editor prompts you to specify the compositions within a draft-enabled business object with with draft;.
Code snippet with root entity, child entity, and association to master entity

The root entity of a business object is always defined as lock master and, if etag or authorization are specified, this is always with addition master.

For child entities, syntax options lock dependent by etag dependent by and authorization dependent by are available, each followed by the name of an association that points to the related master entity.

The following rules apply:

lock
  • Currently, only root entities are allowed as lock master,
  • Lock dependent is mandatory for child entities in managed scenarios,
  • The association always points to root entity.
etag
  • Child entities can be dependent on master.
  • Child entities with etag master have to define an own etag field.
  • Association can point to non-root entity that is higher in the hierarchy.
authorization
  • Currently, only root entities are allowed as authorization master.
  • Association always points to root entity.

Note

If an entity is authorization-dependent, the authorization check for update, delete, and create-by-association operations is done as authorization check for update of the master entity. The authorization check for actions, and create-enabled associations that are not compositions, are done in separate methods in the handler class for the dependent entity.

Define a Composite Business Object

In this exercise, you extend your business object for flight travels. Each flight travel becomes a composition of header data (root entity) and several flight travel items (child entity). First you copy and adjust the template repository objects for the flight travel items. Then you define the composition. The template repository objects are located in ABAP package /LRN/S4D437_TEMPLATE.

Note

In this exercise, replace ## with your group number.

Solution

Repository Object TypeRepository Object ID
CDS Data Definition (Model, Root)/LRN/437E_R_TRAVEL
CDS Data Definition (Model, Child)/LRN/437E_R_TRAVELITEM
CDS Behavior Definition (Model)/LRN/437E_R_TRAVEL

Task 1: Copy Template for Data Model (Child Entity)

Create copies of the following repository objects and place them in your own ABAP package ZS4D437_##. For the repository object names, take the name of the template and replace /LRN/437T with Z##. Make sure that the copies refer to each other and not to the template objects.

Template

Repository Object TypeRepository Object ID
Database Table/LRN/437T_TRITEM
CDS Data Definition/LRN/437T_R_TRAVELITEM

Steps

  1. Copy transparent table /LRN/437T_TRITEM to transparent table Z##_TRITEM and activate the new repository object.

    1. In the Project Explorer, expand node /LRN/S4D437_EXERCISE/LRN/S4D437_TEMPLATEDictionaryDatabase Tables.

    2. Right-click /LRN/437T_TRITEM and choose Duplicate....

    3. Enter the name of your own package and the suggested name for the new database table. Then choose Next.

    4. Select the same transport request as before and choose Finish.

    5. Press Ctrl + F3 to activate the development object.

  2. Create a copy of data definition /LRN/437T_R_TRAVELITEM (suggested name: Z##_R_TRAVELITEM).

    1. In the Project Explorer view, locate the data definition /LRN/437T_R_TRAVELITEM in package /LRN/S4D437_TEMPLATE and right-click it to open the context menu.

    2. From the context menu, select Duplicate....

    3. Enter the name of your package and the name for the copy. Then choose Next.

    4. Assign the new object to the same transport request as before and choose Finish.

  3. Replace the database table in the FROM clause with your own database table Z##_TRITEM and activate the new repository object.

    1. After the FROM keyword, replace /lrn/437t_tritem with z##_tritem.

    2. Press Ctrl + F3 to activate the development object.

Task 2: Define the Composition

In the data definitions on data model level Z##_R_TRAVEL and Z##_R_TRAVELITEM, add the required associations to establish a parent-child relation between flight travels and flight travel items.

Steps

  1. Edit the data definition for your child entity Z##_R_TRAVELITEM. Add the statement association to parent, followed by the name of your root entity Z##_R_Travel.

    1. Adjust the code as follows:

      Code Snippet
      1234
      define root view entity Z##_R_TravelItem as select from z##_tritem association to parent Z##_R_Travel {
  2. Add a meaningful association name (suggested name: _Travel) and the ON condition for the association.

    Hint

    In the ON condition, use the key elements of the parent entity and the corresponding elements in the child entity.
    1. Adjust the code as follows:

      Code Snippet
      123456
      define root view entity Z##_R_TravelItem as select from z##_tritem association to parent Z##_R_Travel as _Travel on $projection.AgencyId = _Travel.AgencyId and $projection.TravelId = _Travel.TravelId {
  3. Perform a syntax check for the data definition and fix the syntax errors.

    1. Press Ctrl + F2 to perform a syntax check and study the errors displayed on the Problems view.

    2. Fix the Root entity cannot define a "TO PARENT" association error by removing the root keyword from the define view entity statement.

    3. Fix the Association _Travel must be included in the selection list error by adding the association name at the end of the element list.

  4. Ignore the The parent entity Z##_R_Travel does not have a composition definition for Z##_R_TravelItem warning and activate the data definition.

    1. Press Ctrl + F3 to activate the development object.

  5. Edit the data definition for the parent entity Z##_R_TRAVEL and add the statement composition of, followed by the name of your child entity Z##_R_TravelItem.

    1. Adjust the code as follows:

      Code Snippet
      1234
      define root view entity Z##_R_Travel as select from z##_travel composition of Z##_R_TravelItem {
  6. Add a suitable cardinality and a meaningful association name (suggested name: _TravelItem).

    1. Adjust the code as follows:

      Code Snippet
      1234
      define root view entity Z##_R_Travel as select from z##_travel composition [0..*] of Z##_R_TravelItem as _TravelItem {
  7. Add the composition at the end of the element list of the view entity.

    1. Adjust the code as follows:

      Code Snippet
      1234
      @Semantics.systemDateTime.localInstanceLastChangedAt: true loc_changed_at as LocChangedAt, _TravelItem }
  8. Activate the data definition.

    1. Press Ctrl + F3 to activate the development object.

Task 3: Add the Composition to the Behavior

Add the child entity to the behavior definition on data model level Z##_R_TRAVEL. Complete the behavior definition to remove all syntax errors and warnings. In particular, link the child entity to its persistent table Z##_TRITEM and a (generated) draft table (suggested name: Z##_TRITEM_D). Define the authorization handling and lock handling for the child entity as being dependent on the root entity, but specify a dedicated etag field for the child entity. Define the field mapping, static field control and managed internal numbering. Finally, add the associations to the behavior definition.

Steps

  1. Edit your behavior definition on data model level Z##_R_TRAVEL. At the end of the source code, use a code template to insert a new define behavior statement. Replace the entity placeholder with the name of your child entity Z##_R_TravelItem and the alias placeholder with a suitable alias (suggested name: Item).

    1. After the closing bracket at the end, enter a new code row and press Ctrl + Space to invoke code completion.

    2. From the suggestion list, choose defineBehaviorFor - Define Behavior For.

    3. Ajust the code as follows:

      Code Snippet
      1234
      define behavior for Z##_R_TravelItem alias Item { }
  2. Use the persistent table addition to link the behavior definition to your transparent table for travel items Z##_TRITEM and the draft table addition to link the behavior definition to a (not yet existing) draft table for travel items (suggested name: Z##_TRITEM_D).

    1. Adjust the code as follows:

      Code Snippet
      123456
      define behavior for Z##_R_TravelItem alias Item persistent table z##_tritem draft table z##_tritem_d { }
  3. Use a quick fix to generate the draft table and activate it.

    1. Place the cursor on the name of the draft table and choose Ctrl + 1 to invoke the quick assist proposals.

    2. From the list of available quick fixes, choose Create draft table z##_tritem_d for entity z##_r_travelitem.

    3. If desired, adapt the generated description for the draft table and choose Next.

    4. Select the same transport request as before and choose Finish.

    5. Press Ctrl + F3 to activate the development object.

  4. Return to the behavior definition. Use the authorization dependent by and lock dependent by additions to make lock and authorization handling for the child entity dependent from the root entity.

    Hint

    Use the name of the to parent association to reference the root entity as the authorization master and the lock master.
    1. Adjust the code as follows:

      Code Snippet
      12345678
      define behavior for Z##_R_TravelItem alias Item persistent table z##_tritem draft table z##_tritem_d authorization dependent by _Travel lock dependent by _Travel { }
  5. Use the etag master addition to enable optimistic concurrency control based on the timestamp for the last local change to a travel item (field LocChangedAt).

    1. Adjust the code as follows:

      Code Snippet
      123456789
      define behavior for Z##_R_TravelItem alias Item persistent table z##_tritem draft table z##_tritem_d authorization dependent by _Travel lock dependent by _Travel etag master LocChangedAt { }
  6. Add the standard operations update and delete to the behavior definition for the child entity.

    1. Adjust the code as follows:

      Code Snippet
      12345678910
      define behavior for Z##_R_TravelItem alias Item persistent table z##_tritem draft table z##_tritem_d authorization dependent by _Travel lock dependent by _Travel etag master LocChangedAt { update; delete; }
  7. To enable persistence for all elements of your child entity, define the mapping between table field names and CDS view element names.

    Hint

    To reduce typing effort, you may copy the mapping statement from the model solution, that is, behavior definition /LRN/437E_R_TRAVEL.
    1. Within the curly brackets of the behavior definition, add the following code:

      Code Snippet
      123456789101112131415
      mapping for z##_tritem corresponding { ItemUuid = item_uuid; AgencyId = agency_id; TravelId = travel_id; CarrierId = carrier_id; ConnectionId = connection_id; FlightDate = flight_date; BookingId = booking_id; PassengerFirstName = passenger_first_name; PassengerLastName = passenger_last_name; ChangedAt = changed_at; ChangedBy = changed_by; LocChangedAt = loc_changed_at; }
  8. Enable managed internal numbering for the technical key field ItemUuid and set all fields that are used in the definition of the to parent association to read-only.

    1. Within the curly brackets, before the mapping for statement, add the following code:

      Code Snippet
      123456
      field ( readonly, numbering : managed ) ItemUuid; field ( readonly ) AgencyId, TravelId;
  9. Add the to parent association _Travel to the behavior definition of the child entity and the composition _TravelItem to the behavior definition of the root entity. Create-enable the composition when you add it to the behavior for the root entity.

    Note

    Remember that your business object is draft-enabled. Therefore, do not forget to draft-enable the associations.
    1. In the behavior definition for the child entity, add the following code:

      Code Snippet
      1
      association _Travel { with draft; }
    2. In the behavior definition for the root entity, add the following code:

      Code Snippet
      1
      association _TravelItem { create; with draft; }
  10. Activate the behavior definition.

    1. Press Ctrl + F3 to activate the development object.