Checking the Semantic Key

In the ABAP RESTful application programming model, the key of a database table is often made up of the client field and a UUID field, whose value is assigned automatically by the runtime when you create a new instance of the business object. This field combination is sufficient to ensure that the system can identify each record in the table uniquely. However, as well as this technical key, our object also has a semantic key - in this case the combination of airline and flight number, which must also be unique according to the business logic. In order to ensure the uniqueness of this field combination, you must implement your own check in the form of a validation.
You declare validations in the behavior definition of the CDS view entity, and implement them in the behavior implementation class.
Input Checks in the App
As well as checking the semantic key, there are other checks that you need to perform. For example, although the generated app allows you to create, read, update and delete data, it does not yet contain any consistency checks. Consequently, you can create flight connections for airlines that don't exist, or where the departure and destination airports are the same.

To prevent this from happening, you define further validations in the behavior definition, and implement them in the behavior implementation class.
Creating Message Texts
Before you create the validation, you must create the texts that you want to display. You do this using a message class. A message class is a collection of up to 1000 messages that belong to a particular application area. As shown in the figure, each text has a number which identifies the message uniquely within the message class.

To create a new message class, proceed as follows:
Choose File → New → Other… and type message into the filter field.
Double-click the Message Class entry in the hit list, then enter a package, name, and description for the new message class. Choose Next.
Assign the message class to a transport request and choose Finish.
Messages can also contain placeholders, which are replaced with concrete values when the message is displayed. Placeholders are denoted by the ampersand symbol followed by a number. You can use up to four placeholders in each message.
Defining the Validation
To define a validation, you add a validation declaration to the behavior definition of your business object. In this example, the validation should be performed whenever the user saves a data record, and this could be either when they create the record or if they subsequently change it.

When you define the validation in the behavior definition, a warning tells you that the corresponding method does not exist. Use a quick fix (key combination CTRL + 1) to add the method to the behavior implementation. The behavior implementation is a local class within your behavior pool. The method definition contains the addition FOR VALIDATE ON SAVE, which identifies it as the implementation of the validation. It has an importing parameter KEYS. This is an internal table containing the keys of the created or changed objects. You use these to read the actual data that the user has entered.
The addition FOR Connection~CheckSemanticKey links the method with the validation CheckSemanticKey from the behavior definition. Here, Connection is the alias name of the view entity Z_R_CONNECTION.

When you define a validation, you must also create its implementation. This is a method in the behavior pool. The easiest way to do this is to use a quick fix. Position the cursor on the name of the validation and press CTRL + 1. ADT proposes to create the method. Double-click the proposal to create the method.
The Validation Process
Note
Some code examples in this section use SELECT statements inside of loops. This has been done to keep the examples simple. Note that SELECTs in loops can cause performance problems and should be avoided.
When the system triggers a validation, it calls the corresponding implementation. The importing parameter KEYS contains the keys of the data records that have been changed. You use the keys to read the fields of the records that you need using Entity Manipulation Language (EML). EML is a special set of statements in ABAP that allows you to address business objects.
Once you have read the data, you can perform the checks that you need. If the check fails, you will need to issue an appropriate error message and, importantly, tell the framework not to write the changes to the database.

The first task in a validation is to read the user input. You do this using the Entity Manipulation Language (EML) statement READ ENTITIES. The keys of the new data records are passed to the validation using the importing parameter keys.
The fields that you need to validate the semantic key are CarrierID for the airline and ConnectionID for the flight number. You also need the UUID field.
The code snippet uses the corresponding operator and an inline declaration for the result set. Below, you see the equivalent code using explicitly-defined variables, which makes it easier to understand the types that are used.
12345678910111213
DATA read_keys TYPE TABLE FOR READ IMPORT zs4d400_r_connection.
DATA connections TYPE TABLE FOR READ RESULT zs4d400_r_connection.
read_keys = CORRESPONDING #( keys ).
READ ENTITIES OF zs4d400_r_connection IN LOCAL MODE
ENTITY Connection
FIELDS ( uuid CarrierID ConnectionID )
WITH read_keys
RESULT connections.
Once you have read the user input, you can use the values of CarrierID and ConnectionID to see if this semantic key has already been used in another data set than the one you are processing just now. Since the key combination could be in either the active table or the draft table, you need to look in both, and the most efficient way to do this is with a union.

The result set of this query should always be empty. If not, there are more records with the same combination of CarrierID and ConnectionID, this means that the record that the user is currently trying to create is a duplicate, and must be rejected.

If the combination of carrier ID and connection ID already exists, there will be an entry in the table check_result. In this case, you must issue a message.
The first step is to create a message object. You do this using the self-reference me and calling the method new_message( ). The parameters ID, number, and severity are mandatory. ID is the name of the message class that contains the message; number is the message number. Severity classifies the message as a success, information, warning, or error message. The behavior implementation class contains a structured constant ms whose components represent the different severity levels. In this case, you need the severity level ms-error.
The method also has optional importing parameters v1, v2, v3, and v4. You use these to replace placeholders with concrete values. In this example, the placeholder &1 is replaced with the airline code, placeholder &2 is replaced with the flight number.
The result of the method call is an object reference. In the next step, you will pass the object to the runtime so that the error message is returned the OData service and displayed in the app preview.

To make the runtime display a message, you must report it using the reported structure. This is an implicit changing parameter of all validation methods and is a deep structure. It contains a component with the alias name of the business object. This component is an internal table.
To report the message, you must do three things:
- Add the key of the affected record to the internal table. You can do this using the field group %tky. When you group fields like this, you can address the name of the group instead of having to address each field individually.
- Attach the message object to the table. You do this by assigning the object reference of the message object to the %msg component of the internal table.
- Bind the message to the affected field. This ensures that the field is emphasized in the app. This, in turn, helps the user to navigate the app better. You do this using the %element component of the internal table.

In this example, reported_record is a structure with the line type of the internal table reported-connection. You fill the %tky component with the contents of the %tky field group in the data record. This is the structure line that we used as a work area for the internal table containing the data that the user entered. Next, you assign the message object that you created using the new_message( ) method to the %msg component. Finally, to bind the message to the field CarrierID, you use the structure %element. %element is a structure, and contains a component for each element in the view entity. If you set a component to "true", the corresponding input field will be highlighted in the app. You do this using the structured constant if_abap_behv=>mk. This has the component on for checked/true and off for unchecked/false.
You cannot use the global constants abap_true and abap_false at this point, as their data types are not compatible.

As well as issuing the message, you must also tell the runtime not to save the incorrect data. To do this, you use the failed structure of the validation method. Failed is an implicit changing parameter that is present in all validation methods.
To report a record as failed, add its field group %tky to the field group %tky of the internal table failed-Connection.

The next validation checks that the airline that the user has entered actually exists. The first step is to read the user input using the EML statement READ ENTITIES. This time, you only need to read the field CarrierID.

The SELECT SINGLE statement reads data using the CDS view entity /dmo/i_carrier and checks whether the given airline exists. If it does, the value of the global constant abap_true ('X') is placed in the field exists. If exists is initial following the SELECT statement, you must issue a message, report it, and add the record to the failed structure as you did in the previous example.
The final validation checks that the origin and destination airports are different. The first step is to read the user input using a READ ENTITIES statement. This time, the fields AirportFromID and AirportToID are relevant.


If the departure and arrival airports are the same, you must issue the corresponding message and fill the reported and failed structures. The code extract shows the relevant coding to create the message. The coding to fill the reported and failed structures is the same as in the previous examples.