Dear ABAPers,

Probably most of you are aware of persistent classes, meanwhile I don’t know many developers who’re actively using them.

Moreover, I just get a response from SAP:

I would propose to avoid the usage of

persisten classes in projects where the underlying structure of the

persistent class is not stable. Generating new persitent classes

is easy, but maintenance of excisting persitent classes could led in

high effort. For this reason I would propose to take the future

maintenace effort into account when you decide to use or not to use

persistent classes.

After spending 2 years on standard TR-TM solution support, that is persistent classes based, I don’t think it’s such a bad thing.

What I want to reach with my blog is to present my personal ideas about how we can make usage of persistent clasess more attractive.

Let’s speak today about query service:

So every persistent class has embedded interface if_os_ca_persistency. Details are here:

Query Service Components – ABAP – Object Services – SAP Library

This interface has very interesting method: get_persistent_by_query

In fact once generating a class for a table we automtically have query service for it – that sounds promising. Let’s go to example:

Here is a code from a standard DEMO_QUERY_SERVICE program.

 agent = ca_spfli_persistent=>agent.     TRY.         query_manager = cl_os_system=>get_query_manager( ).         query = query_manager->create_query(                   i_filter  = `AIRPFROM = PAR1 AND AIRPTO = PAR2` ).         connections =           agent->if_os_ca_persistency~get_persistent_by_query(                    i_query   = query                    i_par1    = airpfrom                    i_par2    = airpto ).         LOOP AT connections ASSIGNING FIELD-SYMBOL().           connection = CAST #(  ).           result-carrid = connection->get_carrid( ).           result-connid = connection->get_connid( ).           APPEND result TO results.         ENDLOOP.         cl_demo_output=>display( results ).       CATCH cx_root INTO exc.         cl_demo_output=>display( exc->get_text( ) ).     ENDTRY. 

What we can see here is that SAP developers suggest us to use a generic request from a string.

Which critical things I see in this example:

  • we have only 3 parameters
  • there is no reference to DDIC structure that means where-used-list will not work for this statement
  • select-options and ranges are not supported

So after that I decided that if we transform the same example into the code like this we  can make things simplier:

 REPORT ZDEMO_QUERY_SERVICE. tables: spfli. parameters:   p_from type spfli-airpfrom,   p_to type spfli-airpto. select-options:   so_carid for spfli-carrid. start-of-selection.   types: begin of query_ts,     airpfrom type spfli-airpfrom,     airpto type spfli-airpto,     carrid type range of spfli-carrid,   end of query_ts.   data(connections) = zcl_os_api=>select_by_query(     exporting       io_agent     =  ca_spfli_persistent=>agent   " Class-Specific Persistence Interface       is_selection = value query_ts(         airpfrom = p_from         airpto = p_to         carrid = so_carid[]       )     ). 

As you can see from the example I represented query not like a single string, but as local variable of structure type where fields have same names as in source table. Moreover, to support multiple selection, you can define parameter as a range (CARRID).

To perform range selection I decided to convert range to a set of OR statements ( SIGN = ‘I’ ) + set of AND statements (SIGN = ‘E’).

This simple class now let me easily generate simple classes for selection.

1) Generate persistent class

2) Define local variable for query

3) Call query with agent and query structure.

The provided class is just a prototype. If you wish – you can copy it and try to use it.

Please read the continue in next posts:

  • Persistent classes: single get( ) method instead of multiple get_xxx() methods calls
  • Persistent classes: hashing generic query requests

The most actual version of a class is here: ZCL_OS_API

Enjoy =)

 class ZCL_OS_API definition   public   abstract   final   create public . public section.   class-methods SELECT_BY_QUERY     importing       !IO_AGENT type ref to IF_OS_CA_PERSISTENCY       !IS_SELECTION type ANY     changing       !CO_TYPE type ref to CL_ABAP_STRUCTDESCR optional     returning       value(RT_RESULT) type OSREFTAB . protected section. private section.   types:     begin of range_ts,             sign type c length 1,             option type c length 2,             low type string,             high type string,           end of range_ts .   class-methods GET_QUERY_RANGE_VALUE     importing       !IS_RANGE type RANGE_TS       !IO_EXPR type ref to IF_OS_QUERY_EXPR_FACTORY       !IV_NAME type STRING     returning       value(RO_EXPR) type ref to IF_OS_QUERY_FILTER_EXPR .   class-methods GET_QUERY_RANGE     importing       !IT_RANGE type TABLE       !IV_NAME type STRING       !IO_EXPR type ref to IF_OS_QUERY_EXPR_FACTORY     returning       value(RO_EXPR) type ref to IF_OS_QUERY_FILTER_EXPR .   type-pools ABAP .   class-methods IS_RANGE     importing       !IO_TYPE type ref to CL_ABAP_TABLEDESCR     returning       value(RV_RANGE) type ABAP_BOOL .   class-methods GET_QUERY     importing       !IS_SELECTION type ANY     changing       !CO_TYPE type ref to CL_ABAP_STRUCTDESCR optional     returning       value(RO_QUERY) type ref to IF_OS_QUERY . ENDCLASS. CLASS ZCL_OS_API IMPLEMENTATION.   method get_query.     data(lo_query) = cl_os_query_manager=>get_query_manager( )->if_os_query_manager~create_query( ).     data(lo_expr) = lo_query->get_expr_factory( ).     if co_type is not bound.       try.           co_type = cast #( cl_abap_typedescr=>describe_by_data( is_selection  ) ).         catch cx_sy_move_cast_error.           " ToDo: message           return.       endtry.     endif.     data: lt_and type table of ref to if_os_query_filter_expr.     " for each selection criteria     loop at co_type->get_included_view( *            p_level =         ) into data(ls_view).       " parameter or range?       case ls_view-type->kind.         " parameter         when ls_view-type->kind_elem.           field-symbols:  type any.           unassign .           assign component ls_view-name of structure is_selection to .           check  is assigned.           try.               " goes to and condition               append                 lo_expr->create_operator_expr(                     i_attr1                     = ls_view-name                   i_operator                  = 'EQ'                   i_val                       = conv #(  )                 ) to lt_and.             catch cx_os_query_expr_fact_error.    "           endtry.         when ls_view-type->kind_table.           " check: is range?           check is_range( cast #( ls_view-type ) ) eq abap_true.           " must be not initial           field-symbols:  type table.           assign component ls_view-name of structure is_selection to .           check  is assigned.           check  is not initial.           " goes to and condition           append get_query_range(             iv_name = ls_view-name             it_range =              io_expr = lo_expr )  to lt_and.       endcase.     endloop.     " build and conditions     loop at lt_and into data(lo_and).       if sy-tabix eq 1.         data(lo_filter) = lt_and[ 1 ].       else.         lo_filter = lo_expr->create_and_expr(           exporting             i_expr1 = lo_filter             i_expr2 = lo_and         ).       endif.     endloop.     lo_query->set_filter_expr( lo_filter ).     ro_query = lo_query.   endmethod.   method get_query_range.     data: lt_and type table of ref to if_os_query_filter_expr,           lt_or type table of ref to if_os_query_filter_expr.     data: ls_range type range_ts.     " .. for each range value     loop at it_range assigning field-symbol().       move-corresponding exact  to ls_range.       " E = AND, I = OR       case ls_range-sign.         when 'E'.           append io_expr->create_not_expr(             get_query_range_value(              is_range = ls_range              io_expr = io_expr              iv_name = iv_name ) )  to lt_and..         when 'I'.           append get_query_range_value(             is_range = ls_range             io_expr = io_expr             iv_name = iv_name ) to lt_or.       endcase.     endloop.     " First of all combine all OR in to a single expression     loop at lt_or into data(lo_or).       if sy-tabix eq 1.         data(lo_filter_or) = lt_or[ 1 ].       else.         lo_filter_or = io_expr->create_or_expr(           exporting             i_expr1 = lo_filter_or             i_expr2 = lo_or         ).       endif.     endloop.     " make all or statements as one of ANDs     append lo_filter_or to lt_and.     loop at lt_and into data(lo_and).       if sy-tabix eq 1.         ro_expr = lt_and[ 1 ].       else.         ro_expr = io_expr->create_and_expr(           exporting             i_expr1 = ro_expr             i_expr2 = lo_and         ).       endif.     endloop.   endmethod.   method get_query_range_value.     try.         case is_range-option.           " is operator           when             'EQ' or             'NE' or             'LE' or             'LT' or             'GE' or             'GT'            .             ro_expr = io_expr->create_operator_expr(                   i_attr1                     = iv_name                   i_operator                  = conv #( is_range-option )                   i_val                       = is_range-low               ).           " is mask           when 'CP'.             data(lv_pattern) = is_range-low.             replace all occurrences of '*' in lv_pattern with '%'.             ro_expr = io_expr->create_like_expr(               i_attr                      = iv_name               i_pattern                   = lv_pattern *              i_not                       = OSCON_FALSE           ).           " is mask with not           when 'NP'.             lv_pattern = is_range-low.             replace all occurrences of '*' in lv_pattern with '%'.             ro_expr = io_expr->create_like_expr(               i_attr                      = iv_name               i_pattern                   = lv_pattern               i_not                       = oscon_true           ). *            when 'BT'.           when others.             " not supported         endcase.       catch cx_os_query_expr_fact_error.  "     endtry.   endmethod.   method IS_RANGE.     CHECK io_type->table_kind eq io_type->tablekind_std AND           io_type->key_defkind eq io_type->KEYDEFKIND_DEFAULT AND           io_type->key eq value ABAP_KEYDESCR_TAB(            ( name = 'SIGN')            ( name = 'OPTION')            ( name = 'LOW')            ( name = 'HIGH') ).     rv_range = abap_true.   endmethod.   method select_by_query.     " check agent     check io_agent is bound.     try.         " get result by using generated method         rt_result = io_agent->get_persistent_by_query(           exporting             " create query by selection criteria             i_query = get_query(               exporting                 is_selection =  is_selection   " Must be structure               changing                 co_type      = co_type     " Runtime Type Services             )   " Query         ).       catch cx_os_object_not_found.    "       catch cx_os_query_error.    "     endtry.   endmethod. ENDCLASS. 

New NetWeaver Information at SAP.com

Very Helpfull

 

 

User Rating: Be the first one !