ABAP Object Services: Some useful additions
All modern programming language environments have some kind of ORM ( Object Relationship Mechanism ) mechanism. It allows persistence to be represented as programming language objects. In ABAP object services, we have persistent objects to hold data which we’ll save to database and transient objects to hold data in ABAP memory temporarily.
This blog summarizes my experiences in the additions I had to make while using object services in ABAP.
– In the points 1 and 2, I describe two features I wasn’t aware of but found them based on requirements.
– The last three examples under point 3 are enhanced methods I had to create using RTTI as they’re not created “out of box” by persistent generator mechanism – they’re not strictly persistence but I found myself wishing them with my persistent objects.
Just to recap about persistent objects, we can get a persistence reference and set values later. So in the below example, lo_flight is a persistent object and we can update price.
Below are the additions I had to make to get object services working efficiently in my own experience.
- Adding extra ( non-persistent ) fields to a persistent object : What if we need an attribute on the persistent objects not part of the underlying database table. These can be added as an attribute .
And then will show up as an attribute in the “Persistence Representant” view. As seen below, the attribute gets added .
The field gets added as a normal attribute to the class and can be removed (attributes coming from the table can’t be removed as they’re greyed out ).
As an example, I had to identify if a peristent object has been changed and I added an extra field ‘update’ for this purpose.
This can be handy if a transient object is converted into a persistent object . e.g. A screen’s PBO gets the transient object and the PAI can check if the object has been modified to trigger the conversion from a transient object to a persistent object.
2. Transient objects in place of ABAP memory: Using transient objects for structures to store memory within a session (as a replacement for ABAP Memory). Many a times, to transfer data with in a session, we export data into ABAP memory and then import it back again. This is fine but this can be difficult to debug in case the export / import locations are not well documented ( imagine data being exported to ABAP memory from an enhancement deep down in a stack and then trying to debug through to find why it’s not set).
A substitute can be to create transient objects from structures.
Looking at the method definitions.
We can create a transient object.
and then retrieve the values.
However, if the CREATE_TRANSIENT and GET_TRANSIENT are not in the same stack , this will fail . e.g. if the GET_TRANSIENT was called in a V1/V2 update process whereas the CREATE_TRANSIENT was in the main process, GET_TRANSIENT will fail.The below diagram represents it diagrammatically.
We still need to use SAP memory but at-least we can replace ABAP memory export / import calls by TRANSIENT objects.
3. Enhanced methods in persistence classes: The last three enhancements are based on addition of new methods to persistent classes. Like regular classes, methods can be added to them and are retained even with regeneration due to data dictionary modifications.
a) “Persist” transient objects: Converting transient objects into persistent objects: In point 1, if the object was modified, I was converting the transient object into a persistent one.
It is handy to be able to save a transient object into a persistent one. E.g. duing PBO of a screen, a transient object was created to read attributes and if attributes are modified, the save can be triggered by converting transient objects into persistent objects.
The below method can be called over the attributes we’re interested in persisting .
data: ls_method type seocmpname,
* ls_class type seoclsname,
lt_params type abap_parmbind_tab,
ls_param type abap_parmbind,
dref type ref to data,
lo_excep type ref to cx_sy_dyn_call_error, “#EC NEEDED
ls_par type abap_parmname.
field-symbols: <fs_attr_value> type any.
* To call the dynamic set_* methods, we need to populate kind, name and ref to actual value
* Create the dynamic method name : SET_<attribute>
concatenate ‘set_’ im_attr into ls_method.
translate ls_method to upper case. “#EC TRANSLANG
* Populate ref to data value
create data dref type (im_data_element).
assign dref->* to <fs_attr_value> .
<fs_attr_value> = im_attr_val .
ls_param-value = dref.
*We’re only setting values => param type is exporting
ls_param-kind = cl_abap_objectdescr=>exporting.
* Create the dynamic param name to be passed
concatenate ‘i_’ im_attr into ls_par.
translate ls_par to upper case.
ls_param-name = ls_par.
insert ls_param into table lt_params .
* Call the dynamic method
call method me->(ls_method)
catch cx_sy_dyn_call_error into lo_excep.
raise exception type zcx_test_update
exporting textid = zcx_test_update=>dynamic_method_call_error .
b) Convert persistent objects to structure : Sometimes we need to get the structure of persistent objects as there are some operations that can’t be done otherwise e.g. value comparison of all fields. It’s required to convert the peristent objects into structures.
DATA: lrf_structdescr TYPE REF TO cl_abap_structdescr,
lv_method_name TYPE seomtdname,
ls_component TYPE abap_compdescr.
FIELD-SYMBOLS: <fs_component> TYPE ANY.
* Request description of transferred structure
lrf_structdescr ?= cl_abap_typedescr=>describe_by_data( ch_struct ).
* Loop via all components of the transferred structure
LOOP AT lrf_structdescr->components INTO ls_component.
* Set the field symbol to the component of the transferred
ASSIGN COMPONENT ls_component-name OF STRUCTURE ch_surgery
* Compose the name of the GET method
CONCATENATE ‘GET_’ ls_component-name INTO lv_method_name.
* Determine the value of the attribute via a dynamic call of
* the GET method and write the value to the structure
CALL METHOD me->(lv_method_name)
result = <fs_component>.
c) Convert structures to persistent objects: And we sometimes need to convert the structure back to a persistent object.
RT_TEST is a reference to the persistence object.
DATA: lo_rtti_struc TYPE REF TO cl_abap_structdescr,
lt_field_list TYPE ddfields,
attr TYPE string,
attr1 TYPE string,
attr_val TYPE string.
FIELD-SYMBOLS: <fs_field> TYPE dfies,
<fs_attr_val> TYPE ANY,
<fs_attr_val1> TYPE ANY.
lo_rtti_struc ?= cl_abap_structdescr=>describe_by_name( struct_name ).
lt_field_list = lo_rtti_struc->get_ddic_field_list( ).
LOOP AT lt_field_list ASSIGNING <fs_field>.
CONCATENATE ‘me->’ <fs_field>-fieldname INTO attr .
TRANSLATE attr TO UPPER CASE.
ASSIGN (attr) TO <fs_attr_val>.
IF sy-subrc = 0.
attr_val = <fs_attr_val>.
attr = <fs_field>-fieldname.
CONCATENATE ‘RT_TEST-‘ <fs_field>-fieldname INTO attr1 .
ASSIGN (attr1) TO <fs_attr_val1>.
<fs_attr_val1> = attr_val.