Les Abstract Entities sont un type CDS special dans RAP qui ne represente pas de table de base de donnees. Elles servent de structure de donnees pour les parametres d’Action, les resultats de Function et les Custom Queries avec une flexibilite maximale.
Que sont les Abstract Entities ?
Les Abstract Entities sont definies avec define abstract entity et n’existent que comme definition de metadonnees. Elles n’ont pas de source de donnees persistante et sont alimentees en donnees par du code ABAP.
@EndUserText.label: 'Donnees meteo externes"define abstract entity ZA_WeatherData{ City : abap.char(40); Temperature : abap.dec(5,2); Humidity : abap.int2; Conditions : abap.char(50); Timestamp : abap.utclong;}Difference avec les autres types d’entites
| Aspect | CDS View | Custom Entity | Abstract Entity |
|---|---|---|---|
| Definition | define view entity | define custom entity | define abstract entity |
| Source de donnees | Tables de base de donnees | Classe Query Provider | Aucune (structure uniquement) |
| Exposable via OData | Oui | Oui | Uniquement via Actions/Functions |
| Cas d’utilisation | Modelisation de donnees | Sources de donnees externes | Parametres, Resultats |
| Persistance | Base de donnees | Aucune | Aucune |
Scenarios d’utilisation
1. Parametres d’Action
Les Abstract Entities definissent des structures d’entree complexes pour les RAP Actions :
@EndUserText.label: 'Parametres de reservation"define abstract entity ZA_BookingParameters{ BookingDate : abap.dats; CustomerID : abap.numc(10); ProductID : abap.char(18); Quantity : abap.int4; DiscountCode : abap.char(10); PaymentMethod : abap.char(20);}Utilisation dans la Behavior Definition :
action createBooking parameter ZA_BookingParameters result [1] $self;2. Resultats de Function
Pour des valeurs de retour complexes des RAP Functions :
@EndUserText.label: 'Resultat de calcul de prix"define abstract entity ZA_PriceCalculationResult{ NetPrice : abap.dec(15,2); TaxAmount : abap.dec(15,2); GrossPrice : abap.dec(15,2); Currency : abap.cuky; DiscountApplied : abap_boolean; DiscountPercent : abap.dec(5,2); ValidUntil : abap.dats;}3. Custom Queries sans table de base de donnees
Pour des donnees provenant de systemes externes ou des valeurs calculees :
@EndUserText.label: 'Taux de change"@ObjectModel.query.implementedBy: 'ABAP:ZCL_EXCHANGE_RATE_QUERY"define custom entity ZI_ExchangeRate{ key SourceCurrency : abap.cuky; key TargetCurrency : abap.cuky; ExchangeRate : abap.dec(15,5); RateDate : abap.dats; Provider : abap.char(20);}Custom Query avec if_rap_query_provider
La classe d’implementation de Query alimente l’entite en donnees :
CLASS zcl_exchange_rate_query DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_rap_query_provider.
PRIVATE SECTION. METHODS fetch_rates_from_api RETURNING VALUE(rt_rates) TYPE zt_exchange_rates.ENDCLASS.
CLASS zcl_exchange_rate_query IMPLEMENTATION. METHOD if_rap_query_provider~select. " 1. Verifier si des donnees sont demandees IF io_request->is_data_requested( ) = abap_false. RETURN. ENDIF.
" 2. Recuperer le filtre DATA(lt_filter) = io_request->get_filter( )->get_as_ranges( ).
" 3. Recuperer la pagination DATA(lv_offset) = io_request->get_paging( )->get_offset( ). DATA(lv_page_size) = io_request->get_paging( )->get_page_size( ).
" 4. Recuperer les donnees depuis l'API externe DATA(lt_rates) = fetch_rates_from_api( ).
" 5. Appliquer le filtre DATA lt_result TYPE STANDARD TABLE OF zi_exchangerate. LOOP AT lt_rates INTO DATA(ls_rate). " Verifier les criteres de filtre IF lt_filter IS NOT INITIAL. DATA(lv_match) = abap_true. LOOP AT lt_filter INTO DATA(ls_filter). CASE ls_filter-name. WHEN 'SOURCECURRENCY'. IF NOT ls_rate-source_currency IN ls_filter-range. lv_match = abap_false. ENDIF. WHEN 'TARGETCURRENCY'. IF NOT ls_rate-target_currency IN ls_filter-range. lv_match = abap_false. ENDIF. ENDCASE. ENDLOOP. IF lv_match = abap_false. CONTINUE. ENDIF. ENDIF.
APPEND VALUE #( SourceCurrency = ls_rate-source_currency TargetCurrency = ls_rate-target_currency ExchangeRate = ls_rate-rate RateDate = ls_rate-rate_date Provider = ls_rate-provider ) TO lt_result. ENDLOOP.
" 6. Nombre total pour la pagination IF io_request->is_total_numb_of_rec_requested( ). io_response->set_total_number_of_records( lines( lt_result ) ). ENDIF.
" 7. Appliquer la pagination IF lv_page_size > 0. DATA(lv_to) = lv_offset + lv_page_size. IF lv_to > lines( lt_result ). lv_to = lines( lt_result ). ENDIF. lt_result = VALUE #( FOR i = lv_offset + 1 WHILE i <= lv_to ( lt_result[ i ] ) ). ENDIF.
" 8. Retourner les donnees io_response->set_data( lt_result ). ENDMETHOD.
METHOD fetch_rates_from_api. " Creer le client HTTP TRY. DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement( comm_scenario = 'Z_EXCHANGE_RATE_API" service_id = 'Z_EXCHANGE_RATE_OUTBOUND" ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ). DATA(lo_request) = lo_client->get_http_request( ). lo_request->set_uri_path( '/api/v1/rates' ).
DATA(lo_response) = lo_client->execute( if_web_http_client=>get ). DATA(lv_json) = lo_response->get_text( ).
" Parser le JSON /ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = rt_rates ).
CATCH cx_root INTO DATA(lx_error). " Logger l'erreur, retourner une table vide " Ou : RAISE SHORTDUMP ENDTRY. ENDMETHOD.ENDCLASS.Integration avec des APIs externes
Connexion API REST
METHOD fetch_weather_data. TRY. DATA(lo_dest) = cl_http_destination_provider=>create_by_comm_arrangement( comm_scenario = 'Z_WEATHER_API" ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_dest ). DATA(lo_request) = lo_client->get_http_request( ).
" Definir les parametres de requete lo_request->set_uri_path( |/weather?city={ iv_city }&units=metric| ). lo_request->set_header_field( i_name = 'Accept" i_value = 'application/json" ).
DATA(lo_response) = lo_client->execute( if_web_http_client=>get ).
IF lo_response->get_status( )-code = 200. DATA(lv_json) = lo_response->get_text( ). parse_weather_json( EXPORTING iv_json = lv_json IMPORTING es_weather = rs_weather ). ENDIF.
CATCH cx_http_dest_provider_error cx_web_http_client_error INTO DATA(lx_error). " Gestion des erreurs ENDTRY.ENDMETHOD.Connexion RFC
METHOD fetch_from_legacy_system. DATA: lv_message TYPE c LENGTH 200.
TRY. DATA(lo_dest) = cl_rfc_destination_provider=>create_by_cloud_destination( i_name = 'SAP_ECC_SYSTEM" ).
CALL FUNCTION 'Z_GET_PRODUCT_STOCK" DESTINATION lo_dest->get_destination_name( ) EXPORTING iv_material = iv_material IMPORTING ev_stock = rv_stock EXCEPTIONS communication_failure = 1 MESSAGE lv_message system_failure = 2 MESSAGE lv_message OTHERS = 3.
IF sy-subrc <> 0. " Logger l'erreur ENDIF.
CATCH cx_rfc_dest_provider_error INTO DATA(lx_error). " Destination non trouvee ENDTRY.ENDMETHOD.Exemple complexe : Donnees de tableau de bord
Un tableau de bord qui agrege des donnees provenant de plusieurs sources :
Definition de l’Abstract Entity
@EndUserText.label: 'KPIs du tableau de bord"@ObjectModel.query.implementedBy: 'ABAP:ZCL_DASHBOARD_QUERY"define custom entity ZI_DashboardKPI{ key KPIId : abap.char(20); KPIName : abap.char(60); KPIValue : abap.dec(15,2); KPIUnit : abap.char(10); Trend : abap.int1; // -1, 0, 1 TrendPercent : abap.dec(5,2); ComparisonValue : abap.dec(15,2); Status : abap.char(10); // Good, Warning, Critical Category : abap.char(30); LastUpdated : abap.utclong;}Implementation de la Query
CLASS zcl_dashboard_query DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_rap_query_provider.
PRIVATE SECTION. METHODS get_sales_kpis RETURNING VALUE(rt_kpis) TYPE zt_dashboard_kpis. METHODS get_inventory_kpis RETURNING VALUE(rt_kpis) TYPE zt_dashboard_kpis. METHODS get_customer_kpis RETURNING VALUE(rt_kpis) TYPE zt_dashboard_kpis. METHODS calculate_trend IMPORTING iv_current TYPE decfloat34 iv_previous TYPE decfloat34 EXPORTING ev_trend TYPE i ev_percent TYPE decfloat34.ENDCLASS.
CLASS zcl_dashboard_query IMPLEMENTATION. METHOD if_rap_query_provider~select. IF io_request->is_data_requested( ) = abap_false. RETURN. ENDIF.
" Agreger les donnees de differentes sources DATA lt_kpis TYPE STANDARD TABLE OF zi_dashboardkpi.
" KPIs ventes depuis CDS View APPEND LINES OF get_sales_kpis( ) TO lt_kpis.
" KPIs inventaire depuis systeme legacy via RFC APPEND LINES OF get_inventory_kpis( ) TO lt_kpis.
" KPIs clients depuis API externe APPEND LINES OF get_customer_kpis( ) TO lt_kpis.
" Appliquer le filtre DATA(lt_filter) = io_request->get_filter( )->get_as_ranges( ). IF lt_filter IS NOT INITIAL. LOOP AT lt_kpis ASSIGNING FIELD-SYMBOL(<ls_kpi>). LOOP AT lt_filter INTO DATA(ls_filter). CASE ls_filter-name. WHEN 'CATEGORY'. IF NOT <ls_kpi>-Category IN ls_filter-range. DELETE lt_kpis. ENDIF. ENDCASE. ENDLOOP. ENDLOOP. ENDIF.
IF io_request->is_total_numb_of_rec_requested( ). io_response->set_total_number_of_records( lines( lt_kpis ) ). ENDIF.
io_response->set_data( lt_kpis ). ENDMETHOD.
METHOD get_sales_kpis. " Periode actuelle et precedente depuis CDS View SELECT SUM( net_amount ) AS current_sales FROM zi_salesorder WHERE created_at >= @( cl_abap_context_info=>get_system_date( ) - 30 ) INTO @DATA(lv_current_sales).
SELECT SUM( net_amount ) AS previous_sales FROM zi_salesorder WHERE created_at >= @( cl_abap_context_info=>get_system_date( ) - 60 ) AND created_at < @( cl_abap_context_info=>get_system_date( ) - 30 ) INTO @DATA(lv_previous_sales).
DATA: lv_trend TYPE i, lv_percent TYPE decfloat34.
calculate_trend( EXPORTING iv_current = lv_current_sales iv_previous = lv_previous_sales IMPORTING ev_trend = lv_trend ev_percent = lv_percent ).
APPEND VALUE #( KPIId = 'SALES_30D" KPIName = 'Chiffre d''affaires (30 jours)" KPIValue = lv_current_sales KPIUnit = 'EUR" Trend = lv_trend TrendPercent = lv_percent ComparisonValue = lv_previous_sales Status = COND #( WHEN lv_trend >= 0 THEN 'Good" WHEN lv_percent > -10 THEN 'Warning" ELSE 'Critical' ) Category = 'Sales" LastUpdated = utclong_current( ) ) TO rt_kpis. ENDMETHOD.
METHOD calculate_trend. IF iv_previous = 0. ev_trend = 0. ev_percent = 0. ELSE. ev_percent = ( iv_current - iv_previous ) / iv_previous * 100. ev_trend = COND #( WHEN ev_percent > 0 THEN 1 WHEN ev_percent < 0 THEN -1 ELSE 0 ). ENDIF. ENDMETHOD.
" Autres methodes...ENDCLASS.Bonnes pratiques pour la performance
1. Implementer la mise en cache
CLASS zcl_cached_query DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_rap_query_provider.
PRIVATE SECTION. CLASS-DATA: gt_cache TYPE zt_cached_data, gv_cache_timestamp TYPE utclong.
CLASS-METHODS is_cache_valid RETURNING VALUE(rv_valid) TYPE abap_bool. METHODS refresh_cache.
CONSTANTS: gc_cache_duration_seconds TYPE i VALUE 300. " 5 minutesENDCLASS.
CLASS zcl_cached_query IMPLEMENTATION. METHOD is_cache_valid. IF gv_cache_timestamp IS INITIAL. rv_valid = abap_false. RETURN. ENDIF.
DATA(lv_age) = utclong_diff( high = utclong_current( ) low = gv_cache_timestamp ).
rv_valid = xsdbool( lv_age < gc_cache_duration_seconds ). ENDMETHOD.
METHOD if_rap_query_provider~select. IF io_request->is_data_requested( ) = abap_false. RETURN. ENDIF.
" Verifier le cache IF is_cache_valid( ) = abap_false. refresh_cache( ). ENDIF.
" Retourner les donnees depuis le cache io_response->set_data( gt_cache ). ENDMETHOD.
METHOD refresh_cache. " Recuperer les donnees externes gt_cache = fetch_external_data( ). gv_cache_timestamp = utclong_current( ). ENDMETHOD.ENDCLASS.2. Traitement parallele
METHOD fetch_data_parallel. " Recuperer plusieurs sources de donnees en parallele DATA: lt_tasks TYPE TABLE OF REF TO zcl_data_fetch_task.
" Creer les taches APPEND NEW zcl_data_fetch_task( 'SOURCE_A' ) TO lt_tasks. APPEND NEW zcl_data_fetch_task( 'SOURCE_B' ) TO lt_tasks. APPEND NEW zcl_data_fetch_task( 'SOURCE_C' ) TO lt_tasks.
" Executer en parallele DATA(lo_parallel) = NEW cl_abap_parallel( ). lo_parallel->run_instances( EXPORTING p_in_tab = lt_tasks IMPORTING p_out_tab = DATA(lt_results) ).
" Agreger les resultats LOOP AT lt_results INTO DATA(lo_result). APPEND LINES OF lo_result->get_data( ) TO rt_combined_data. ENDLOOP.ENDMETHOD.3. Gestion des erreurs
METHOD if_rap_query_provider~select. TRY. " Recuperer les donnees DATA(lt_data) = fetch_external_data( ). io_response->set_data( lt_data ).
CATCH cx_http_dest_provider_error cx_web_http_client_error INTO DATA(lx_http_error). " Erreur HTTP : Reponse vide ou donnees de secours io_response->set_data( VALUE #( ) ).
" Optionnel : Lever une Business Exception RAISE EXCEPTION TYPE zcx_external_service_error EXPORTING textid = zcx_external_service_error=>service_unavailable service_name = 'Weather API'.
CATCH cx_root INTO DATA(lx_error). " Logger l'erreur generique cl_bali_log=>create_with_header( EXPORTING header = cl_bali_header_setter=>create( object = 'ZDASHBOARD" subobject = 'QUERY" ) )->add_item( item = cl_bali_exception_setter=>create( exception = lx_error ) )->save( ).
io_response->set_data( VALUE #( ) ). ENDTRY.ENDMETHOD.Abstract Entities vs. Custom Entities
| Utilisation | Recommandation |
|---|---|
| Parametres d’Action | Abstract Entity |
| Resultats de Function | Abstract Entity |
| Service OData avec filtrage | Custom Entity |
| Donnees en lecture seule | Custom Entity |
| Structures temporaires | Abstract Entity |
| Donnees de tableau de bord | Custom Entity avec cache |
Resume
Les Abstract Entities et les Custom Entities avec if_rap_query_provider offrent une flexibilite maximale pour :
- Sources de donnees externes - APIs REST, RFC, systemes legacy
- Donnees calculees - Agregations, KPIs, tendances
- Parametres Action/Function - Structures d’entree/sortie complexes
- Solutions hybrides - Combinaison de base de donnees et sources externes
Points importants :
- Toujours implementer la mise en cache pour les sources de donnees externes
- Prendre en compte la pagination et le filtrage dans le Query Provider
- Gestion des erreurs robuste avec strategies de secours
- Optimiser la performance par le traitement parallele
Sujets connexes
- RAP Custom Entities - Custom Entities avec sources de donnees externes
- RAP Fondamentaux - Le modele de programmation RAP
- Client HTTP - Appeler des APIs REST dans ABAP Cloud