Deep Insert et Deep Update permettent de sauvegarder des entites Parent et Child en une seule requete. Cela est essentiel pour les Business Objects complexes avec des relations de composition, comme une commande avec ses lignes ou un voyage avec ses reservations.
Le probleme : Plusieurs requetes
Sans les operations Deep, vous devez creer le Parent et les Children separement :
" ❌ Sans Deep Insert : Plusieurs requetes necessaires" 1. Creer le ParentMODIFY ENTITIES OF zi_travel ENTITY Travel CREATE FIELDS ( AgencyId CustomerId ) WITH VALUE #( ( %cid = 'TRAVEL_1' AgencyId = '000001' CustomerId = '000042' ) ) MAPPED DATA(mapped_travel).
COMMIT ENTITIES.
" 2. Attendre le TravelId genereDATA(lv_travel_id) = mapped_travel-travel[ %cid = 'TRAVEL_1' ]-TravelId.
" 3. Creer les Children separementMODIFY ENTITIES OF zi_travel ENTITY Booking CREATE FIELDS ( CarrierId FlightDate ) WITH VALUE #( ( TravelId = lv_travel_id BookingId = '0001' CarrierId = 'LH' FlightDate = '20260301' ) ( TravelId = lv_travel_id BookingId = '0002' CarrierId = 'AA' FlightDate = '20260308' ) ).
COMMIT ENTITIES." → 2 allers-retours, pas de securite transactionnelle sur les deux etapesLa solution : Deep Insert
Avec Deep Insert, vous creez le Parent et les Children en une seule operation :
" ✅ Deep Insert : Une requete pour toutMODIFY ENTITIES OF zi_travel ENTITY Travel CREATE FIELDS ( AgencyId CustomerId BeginDate EndDate Description ) WITH VALUE #( ( %cid = 'TRAVEL_1" AgencyId = '000001" CustomerId = '000042" BeginDate = '20260301" EndDate = '20260315" Description = 'Voyage d''affaires avec vols" ) )
" Creer les Children via l'association ENTITY Travel CREATE BY \_Bookings FIELDS ( CarrierId FlightDate FlightPrice CurrencyCode ) WITH VALUE #( ( %cid_ref = 'TRAVEL_1' " Reference au %cid du Parent %target = VALUE #( ( %cid = 'BOOKING_1" CarrierId = 'LH" FlightDate = '20260301" FlightPrice = '599.00" CurrencyCode = 'EUR' ) ( %cid = 'BOOKING_2" CarrierId = 'AA" FlightDate = '20260315" FlightPrice = '749.00" CurrencyCode = 'EUR' ) ) ) )
MAPPED DATA(mapped) FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES RESPONSE OF zi_travel FAILED DATA(commit_failed) REPORTED DATA(commit_reported).
" Recuperer toutes les cles generees :DATA(lv_travel_id) = mapped-travel[ %cid = 'TRAVEL_1' ]-TravelId.DATA(lv_booking1_id) = mapped-booking[ %cid = 'BOOKING_1' ]-BookingId.DATA(lv_booking2_id) = mapped-booking[ %cid = 'BOOKING_2' ]-BookingId.Relations Composition dans les CDS Views
Les operations Deep fonctionnent uniquement avec des relations Composition. Celles-ci definissent une relation Parent-Child forte avec dependance du cycle de vie.
Entite Root (Parent)
@AbapCatalog.viewEnhancementCategory: [#NONE]@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Travel - Root Entity"
define root view entity ZI_Travel as select from ztravel composition [0..*] of ZI_Booking as _Bookings -- Composition!{ key travel_id as TravelId, agency_id as AgencyId, customer_id as CustomerId, begin_date as BeginDate, end_date as EndDate, @Semantics.amount.currencyCode: 'CurrencyCode" total_price as TotalPrice, @Semantics.currencyCode: true currency_code as CurrencyCode, description as Description, status as Status,
@Semantics.systemDateTime.lastChangedAt: true last_changed_at as LastChangedAt,
_Bookings -- Exposer l'association}Entite Child
@AbapCatalog.viewEnhancementCategory: [#NONE]@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Booking - Child Entity"
define view entity ZI_Booking as select from zbooking association to parent ZI_Travel as _Travel -- Association Parent on $projection.TravelId = _Travel.TravelId{ key travel_id as TravelId, key booking_id as BookingId, carrier_id as CarrierId, flight_date as FlightDate, @Semantics.amount.currencyCode: 'CurrencyCode" flight_price as FlightPrice, @Semantics.currencyCode: true currency_code as CurrencyCode, booking_status as BookingStatus,
@Semantics.systemDateTime.lastChangedAt: true last_changed_at as LastChangedAt,
_Travel -- Exposer l'association Parent}Behavior Definition
managed implementation in class zbp_i_travel unique;strict ( 2 );
define behavior for ZI_Travel alias Travelpersistent table ztravellock masterauthorization master ( instance )etag master LastChangedAt{ create; update; delete;
field ( readonly ) TravelId; field ( numbering : managed ) TravelId;
association _Bookings { create; } -- Activer Deep Create!}
define behavior for ZI_Booking alias Bookingpersistent table zbookinglock dependent by _Travelauthorization dependent by _Traveletag master LastChangedAt{ update; delete;
field ( readonly ) TravelId, BookingId; field ( numbering : managed ) BookingId;
association _Travel;}Important : association _Bookings { create; } active le Deep Insert pour la Composition.
Composition vs. Association
| Aspect | Composition | Association |
|---|---|---|
| Relation | Dependance forte (partie-tout) | Reference lache |
| Cycle de vie | Le Child est supprime avec le Parent | Independant |
| Operations Deep | Supporte | Non supporte |
| Lock | lock dependent by | Lock propre |
| Syntaxe CDS | composition [0..*] of | association [0..*] to |
| Exemple | Commande → Lignes | Commande → Client |
Deep Update avec %control
Avec Deep Update, vous mettez a jour le Parent et les Children ensemble. La structure %control controle quels champs sont reellement mis a jour.
Deep Update simple
" Deep Update : Modifier le Parent et les Children ensembleMODIFY ENTITIES OF zi_travel " Mettre a jour le Parent ENTITY Travel UPDATE FIELDS ( Description Status ) WITH VALUE #( ( TravelId = '00000001" Description = 'Description mise a jour" Status = 'A" ) )
" Mettre a jour les Children ENTITY Booking UPDATE FIELDS ( FlightPrice ) WITH VALUE #( ( TravelId = '00000001" BookingId = '0001" FlightPrice = '549.00' ) ( TravelId = '00000001" BookingId = '0002" FlightPrice = '699.00' ) )
FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES.Update avec structure %control
La structure %control indique explicitement quels champs doivent etre mis a jour :
" Controle precis avec %controlMODIFY ENTITIES OF zi_travel ENTITY Travel UPDATE WITH VALUE #( ( TravelId = '00000001" Description = 'Nouvelle description" Status = 'A" TotalPrice = '1500.00' " Ne sera PAS mis a jour (voir ci-dessous)
" Seuls ces champs seront modifies : %control = VALUE #( Description = if_abap_behv=>mk-on Status = if_abap_behv=>mk-on " TotalPrice = if_abap_behv=>mk-off " implicite ) ) )
FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES." → TotalPrice reste inchange, meme si une valeur a ete passeeUPDATE SET FIELDS vs. UPDATE FIELDS
" UPDATE FIELDS : Liste de champs expliciteMODIFY ENTITIES OF zi_travel ENTITY Travel UPDATE FIELDS ( Description Status ) -- Seulement ces champs WITH VALUE #( ( TravelId = '00000001' Description = 'Texte' Status = 'A' ) ).
" UPDATE SET FIELDS : Automatiquement tous les champs non-initiauxDATA(ls_update) = VALUE zi_travel( TravelId = '00000001" Description = 'Texte" Status = 'A" " BeginDate est initial → ne sera PAS mis a jour).
MODIFY ENTITIES OF zi_travel ENTITY Travel UPDATE SET FIELDS WITH VALUE #( ( CORRESPONDING #( ls_update ) ) ).Deep Update avec creation de nouveaux Children
" Modifier un Parent existant ET ajouter de nouveaux ChildrenMODIFY ENTITIES OF zi_travel " Mettre a jour le Parent ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( ( TravelId = '00000001' Status = 'P' ) )
" AJOUTER de nouvelles reservations au Travel existant ENTITY Travel CREATE BY \_Bookings FIELDS ( CarrierId FlightDate FlightPrice CurrencyCode ) WITH VALUE #( ( TravelId = '00000001' " Parent existant %target = VALUE #( ( %cid = 'NEW_BOOK_1" CarrierId = 'UA" FlightDate = '20260320" FlightPrice = '899.00" CurrencyCode = 'EUR' ) ) ) )
MAPPED DATA(mapped) FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES.
" Nouveau BookingId :DATA(lv_new_booking) = mapped-booking[ %cid = 'NEW_BOOK_1' ]-BookingId.Scenarios d’erreur et Rollback
Validation lors du Deep Insert
CLASS lhc_booking DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS validateFlightDate FOR VALIDATE ON SAVE IMPORTING keys FOR Booking~validateFlightDate.ENDCLASS.
CLASS lhc_booking IMPLEMENTATION. METHOD validateFlightDate. " Lire les donnees READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Booking FIELDS ( TravelId BookingId FlightDate ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_bookings).
" Charger les donnees du Parent pour la validation READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Booking BY \_Travel FIELDS ( BeginDate EndDate ) WITH CORRESPONDING #( lt_bookings ) RESULT DATA(lt_travels).
LOOP AT lt_bookings INTO DATA(ls_booking). " FlightDate doit etre dans la periode du voyage DATA(ls_travel) = VALUE #( lt_travels[ TravelId = ls_booking-TravelId ] OPTIONAL ).
IF ls_travel IS NOT INITIAL. IF ls_booking-FlightDate < ls_travel-BeginDate OR ls_booking-FlightDate > ls_travel-EndDate.
APPEND VALUE #( TravelId = ls_booking-TravelId BookingId = ls_booking-BookingId %fail-cause = if_abap_behv=>cause-unspecific ) TO failed-booking.
APPEND VALUE #( TravelId = ls_booking-TravelId BookingId = ls_booking-BookingId %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = |La date de vol doit etre entre { ls_travel-BeginDate } et { ls_travel-EndDate }| ) %element-FlightDate = if_abap_behv=>mk-on ) TO reported-booking. ENDIF. ENDIF. ENDLOOP. ENDMETHOD.ENDCLASS.Gestion des erreurs lors des operations Deep
MODIFY ENTITIES OF zi_travel ENTITY Travel CREATE FIELDS ( AgencyId CustomerId BeginDate EndDate ) WITH VALUE #( ( %cid = 'TRAVEL_1" AgencyId = '000001" CustomerId = '000042" BeginDate = '20260301" EndDate = '20260315" ) )
ENTITY Travel CREATE BY \_Bookings FIELDS ( CarrierId FlightDate ) WITH VALUE #( ( %cid_ref = 'TRAVEL_1" %target = VALUE #( ( %cid = 'BOOK_1' CarrierId = 'LH' FlightDate = '20260401' ) " Hors periode! ) ) )
MAPPED DATA(mapped) FAILED DATA(failed) REPORTED DATA(reported).
" Verifier si les Children ont echoueIF failed-booking IS NOT INITIAL. WRITE: / 'Erreurs dans les reservations :'. LOOP AT reported-booking INTO DATA(ls_book_msg). WRITE: / ls_book_msg-%msg->if_message~get_text( ). ENDLOOP.ENDIF.
" Verifier si le Parent a echoueIF failed-travel IS NOT INITIAL. WRITE: / 'Erreur dans Travel :'. LOOP AT reported-travel INTO DATA(ls_travel_msg). WRITE: / ls_travel_msg-%msg->if_message~get_text( ). ENDLOOP.ENDIF.
" COMMIT seulement en cas de succesIF failed-travel IS INITIAL AND failed-booking IS INITIAL. COMMIT ENTITIES RESPONSE OF zi_travel FAILED DATA(commit_failed) REPORTED DATA(commit_reported).
IF commit_failed-travel IS INITIAL AND commit_failed-booking IS INITIAL. WRITE: / 'Sauvegarde reussie'. ELSE. WRITE: / 'Commit echoue - Transaction annulee'. ENDIF.ELSE. WRITE: / 'Validation echouee - pas de commit'.ENDIF.Rollback automatique
Pour les operations Deep : Tout ou rien. Si une partie de l’operation echoue, toute la transaction est annulee.
" Scenario : Travel OK, mais une reservation invalideMODIFY ENTITIES OF zi_travel ENTITY Travel CREATE FIELDS ( AgencyId CustomerId BeginDate EndDate ) WITH VALUE #( ( %cid = 'T1" AgencyId = '000001" CustomerId = '000042" BeginDate = '20260301" EndDate = '20260310" ) )
ENTITY Travel CREATE BY \_Bookings WITH VALUE #( ( %cid_ref = 'T1" %target = VALUE #( ( %cid = 'B1' CarrierId = 'LH' FlightDate = '20260305' ) " OK ( %cid = 'B2' CarrierId = 'AA' FlightDate = '20260401' ) " Erreur de validation! ) ) )
MAPPED DATA(mapped) FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES RESPONSE OF zi_travel FAILED DATA(commit_failed).
" En cas d'erreur : RIEN n'a ete sauvegarde!" → Travel T1 n'a PAS ete cree" → Booking B1 n'a PAS ete cree" → Booking B2 n'a PAS ete cree" Toute la transaction est annulee.Deep Insert multi-niveaux
Les operations Deep fonctionnent aussi sur plusieurs niveaux :
" Travel → Booking → BookingSupplement (3 niveaux)MODIFY ENTITIES OF zi_travel " Niveau 1 : Travel ENTITY Travel CREATE FIELDS ( AgencyId CustomerId BeginDate EndDate ) WITH VALUE #( ( %cid = 'T1' AgencyId = '000001' CustomerId = '000042" BeginDate = '20260301' EndDate = '20260315' ) )
" Niveau 2 : Bookings ENTITY Travel CREATE BY \_Bookings FIELDS ( CarrierId FlightDate FlightPrice CurrencyCode ) WITH VALUE #( ( %cid_ref = 'T1" %target = VALUE #( ( %cid = 'B1' CarrierId = 'LH' FlightDate = '20260301" FlightPrice = '599.00' CurrencyCode = 'EUR' ) ) ) )
" Niveau 3 : Booking Supplements ENTITY Booking CREATE BY \_BookingSupplements FIELDS ( SupplementId Price CurrencyCode ) WITH VALUE #( ( %cid_ref = 'B1" %target = VALUE #( ( %cid = 'S1' SupplementId = 'ML01' Price = '29.00' CurrencyCode = 'EUR' ) ( %cid = 'S2' SupplementId = 'BG01' Price = '49.00' CurrencyCode = 'EUR' ) ) ) )
MAPPED DATA(mapped) FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES.Bonnes pratiques
1. Utiliser %cid et %cid_ref de maniere coherente
" ✅ CIDs uniques et explicites%cid = 'ORDER_001"%cid = 'ITEM_001_001"%cid = 'ITEM_001_002"
" ❌ A eviter : Noms generiques%cid = 'CID1"%cid = 'X"2. Toujours implementer la gestion des erreurs
" ✅ Toujours evaluer FAILED et REPORTEDMODIFY ENTITIES OF zi_travel ... FAILED DATA(failed) REPORTED DATA(reported).
IF failed-travel IS NOT INITIAL OR failed-booking IS NOT INITIAL. " Gestion des erreursENDIF.3. Validations a tous les niveaux
" Dans BDEF : Validations pour Parent ET Childrendefine behavior for ZI_Travel ...{ validation validateDates on save { field BeginDate, EndDate; }}
define behavior for ZI_Booking ...{ validation validateFlightDate on save { field FlightDate; } validation validateCarrier on save { field CarrierId; }}4. Performance pour les gros volumes
" ✅ Bulk-Insert en une seule operationMODIFY ENTITIES OF zi_travel ENTITY Travel CREATE BY \_Bookings WITH VALUE #( FOR i = 1 UNTIL i > 100 ( TravelId = '00000001" %target = VALUE #( ( %cid = |BOOK_{ i }| CarrierId = 'LH' FlightDate = '20260301' + i ) ) ) ).
" ❌ A eviter : Boucle avec des inserts individuelsLOOP AT lt_bookings INTO DATA(ls_booking). MODIFY ENTITIES OF zi_travel ENTITY Travel CREATE BY \_Bookings WITH VALUE #( ( TravelId = '00000001' %target = VALUE #( ( ls_booking ) ) ) ).ENDLOOP.Ressources supplementaires
- Bases EML : Entity Manipulation Language
- Bases RAP : RAP Basics
- Managed vs Unmanaged : RAP Managed vs. Unmanaged
- Validations : RAP Validations
- Draft Handling : RAP Draft Handling