Implementing Unmanaged Business Objects

Objective

After completing this lesson, you will be able to implement an unmanaged business object.

Methods for Standard Operations

The behavior pool for the unmanaged implementation

A main characteristic of the unmanaged implementation scenario is the fact that the standard operations are implemented in the local handler classes. While the read method exists independently from the behavior definition, the create, update, and delete methods only exist if the related standard operation is defined in the behavior definition.

Similarly, the lock method exists only if the related BO entity is defined as lock master.

Implementation of the standard operations

All methods for standard operation implementation have exactly one importing parameter. In the create and update methods, this parameter is named entities. In the other methods, the parameter name is keys. The read method is the only one with a result parameter.

Note

The different importing parameter names indicate that for the Create and Update operation, more is needed as input than just the identification of the affected entity instances.

Although not mentioned in the method signature, the methods have implicitly defined changing parameters for the response. The failed and reported parameters exist in all methods. The mapped parameter only exists in the create and update methods.

All parameters, the explicitly defined ones, and the implicitly defined response parameters, are typed with the derived types that are also used when accessing the business object using EML.

Note

The use of the same derived types makes it obvious that in the unmanaged implementation scenario, the BO runtime more or less relays the requests from the consumer to the behavior implementation. It then returns the result and response to the caller.

Methods for the Save Sequence

Methods of the saver class

The saver class always inherits from the global CL_ABAP_BEHAVIOR_SAVER class. To implement the saver class, you redefine and implement inherited methods of the superclass. The framework calls these methods at predefined points in time along the save sequence. In the unmanaged implementation scenario, it is mandatory to redefine the save method and implement it with the write accesses to the database. Redefining the other methods is optional.

The save sequence starts with finalize( ) performing the final calculations before data can be persisted. If the subsequent check_before_save( ) call is positive for all transactional changes, the point-of-no-return is reached. From now on, a successful save( ) must be guaranteed by all involved BOs. After the point-of-no-return, the adjust_numbers( ) call can occur to take care of late numbering. The save( ) call persists all BO instance data from the transactional buffer in the database. The final cleanup( ) call clears the transactional buffer.

Note

The cleanup_finalize( ) method is not part of the normal save sequence. It is meant to clear the buffer if either of the methods finalize( ) or check_before_save( ) failed. In other words: cleanup_finalize is called, if the save sequence fails before the point-of-no-return.

Type Mapping

Data transfer between derived types and dictionary types

When you reuse existing code in the behavior implementation of a business object, you must convert data from the derived types used in the RAP world, to the dictionary types used in the signatures of the existing code and vice versa. It is easy if the component names in the derived types and in the existing dictionary types are the same or only differ in upper/lower case, because then you can simply use the CORRESPONDING expression to convert the data. However, most of the time, at least some of the fields have different names in the RAP world and in the existing code.

In the example, the existing code is a method or a function module with a changing parameter cs_struct which is typed with a dictionary structure type. The components of this structure type are called comp1, comp2, and so on. The BO entity in this example defines corresponding view elements named field1, field2, and so on.

Let us consider that we are implementing a behavior implementation method, where the business object data is available in a data object named entity which is based on a derived structure type for this entity. To hand the information over to the existing code, we have to copy it from entity to a data object typed with the dictionary structure type and, because the component names are not identical, we cannot use an ordinary CORRESPONDING expression. We have to copy the data component by component.

Field mapping in behavior definition and ABAP

To facilitate the behavior implementation, ABAP offers special variants of the CORRESPONDING expression which use field mapping that is maintained centrally in the behavior definition.

In the example, the behavior definition contains a mapping statement for the structure type that is used by the existing code. This mapping statement pairs the entity fields on the left and the structure components on the right.

The ABAP code in the behavior implementation defines a data object struct, which is typed with this structure type and a data object entity typed with a derived structure type for the entity.

When copying information from entity to struct, the mapping is used in a CORRESPONDING expression with the addition MAPPING FROM ENTITY. When copying information from struct to entity, the addition MAPPING TO ENTITY is used instead.

Note

This technique is particularly interesting for the unmanaged implementation type, where the behavior implementation represents a kind of a wrapper for existing legacy functionality. But, also in the managed implementation type, we can think of situations where the code for a determination or validation already exists, but is based on "old" (legacy) data types.

Control Mapping

Control type mapping

Often legacy scenarios use a control structure that is second dictionary structure type that contains the same components as the data structure, but all of them have data type C(1). This type is then used to indicate the fields in the main structure that are accessed by an operation (Update, Read, and so on).

When calling existing code that follows this approach from inside a behavior implementation, the actual parameter for the control structure has to be filled with the values from the  %CONTROL structure in the derived types.

As well as the possibly different field names, you have to consider the different concepts for boolean-like types in RAP and in the legacy code. Where ABAP traditionally uses type C(1) with values 'X' for true and ' ' (Space) for false, RAP uses the more modern approach of type X(1) with values hexadecimal values #01 and #00.

It could lead to rather lengthy coding. The example shows only the mapping of the first component of %control to the first component of the legacy control structure structx. You can repeat this code for all other components as well.

Control mapping in behavior definition and ABAP coding

When you define the mapping for a data structure, you can also include a mapping for the control structure. To do so, add keyword control, followed by the name of the legacy control structure type.

In the ABAP code, add USING CONTROL inside the CORRESPONDING expression if you want to fill a legacy control structure based on the %control component of a derived data type.

Note

For the opposite direction, use the following syntax: entity = CORRESPONDING #( structx CHANGING CONTROL ).

How to Implement an Unmanaged Business Object

Analyze the function modules in function group /LRN/437FLCASS. The function module names start with /LRN/437_FLCLASS_. Then use this legacy code to implement the behavior of your unmanaged business object Z##_R_CLASS. Facilitate the implementation by defining a field mapping for the structure types /LRN/437_S_CLASS and /LRN/537_S_CLASST which are used to type the parameters of the function modules.

Steps

  1. Analyze function group /LRN/437FLCASS.

    1. In the behavior pool for your unmanaged business object Z00_R_CLASS, implement the handler class (lhc_class). Start by implementing the DELETE method.

    2. Now, implement the CREATE method. Define a field mapping for structure type /LRN/437_S_CLASS and use it in a CORRESPONDING expression before the call of function module /LRN/437_FLCLASS_CREATE.

    3. Now, implement the method UPDATE. Add the structure type /LRN/437_S_CLASSX to the field mapping as the related control structure and use it in a CORRESPONDING expression before the call of function module /LRN/437_FLCLASS_UPDATE.

    4. Implement the READ method. Do not forget to concatenate the priority and description elements into the allelements element. Explain that the READ method is needed for the optimistic concurrency control (etag handling) of the OData Service.

    5. Stop implementing your own business object. Show the implementations in ABAP class /LRN/BP_437_R_CLASS instead. Highlight the mapping of messages from the function module parameter et_messages to the response parameter reported.

    6. Show the implementation of the SAVE method in the saver class lsc_/lrn/437_r_class.

    7. Set a breakpoint in the SAVE method and in the DELETE, CREATE, UPDATE, READ methods of the handler class lhc_class.

    8. Preview the /LRN/437_UI_CLASS_O2 OData UI service. Create a new entry, update the entry, and delete the entry to illustrate how and when the methods of the implementations are called.