OData Gateway Part 3: $expand, Navigation, Deep Insert and $batch
Handle related data in SEGW services: associations and navigation properties, navigation reads in GET ENTITYSET, $expand via GET EXPANDED ENTITY, deep insert of header with items via CREATE DEEP ENTITY, and $batch with changesets and commit rules.
Explanation
📌 KEY POINTS
✅ Associations and navigation properties link entity types (SalesOrder → ToItems); the client follows them with /SalesOrderSet('4711')/ToItems or $expand=ToItems.
✅ Without custom code, $expand calls GET_ENTITYSET of the child once per parent (N+1 calls); redefining GET_EXPANDED_ENTITY / GET_EXPANDED_ENTITYSET reads everything in one go.
✅ Deep insert (POST header with nested items) is handled in CREATE_DEEP_ENTITY with a deep structure defined in MPC_EXT.
⚠️ $batch groups requests; modifying requests sit in changesets that succeed or fail together. Inside a changeset the framework controls the commit — no COMMIT WORK in your code there.
━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE
Part 1 returned a flat list. Real apps need header and items together. Parts 6–8 cover single reads and CUD in detail; this part focuses on related data.
━━━━━━━━━━━━━━━━━━━━
▶ 1. ASSOCIATION AND NAVIGATION
- SEGW → Data Model → Associations: SalesOrder (1) to SalesOrderItem (N), referential constraint SalesOrder = SalesOrder.
- Navigation property ToItems on SalesOrder.
- Request /SalesOrderSet('4711')/ToItems calls SALESORDERITEMSET_GET_ENTITYSET; the parent key arrives in it_key_tab / io_tech_request_context (source keys).
━━━━━━━━━━━━━━━━━━━━
▶ 2. $EXPAND
- /SalesOrderSet('4711')?$expand=ToItems returns header with nested items.
- Default: framework calls child GET_ENTITYSET per parent — fine for one parent, slow for lists.
- Optimized: redefine SALESORDERSET's GET_EXPANDED_ENTITY (single) or GET_EXPANDED_ENTITYSET (list), fill a deep structure, and report handled navigation in et_expanded_tech_clauses.
━━━━━━━━━━━━━━━━━━━━
▶ 3. DEEP INSERT
- Client POSTs header JSON with nested "ToItems": [ … ].
- Define a deep type in MPC_EXT (header fields + items table named like the navigation property).
- Redefine CREATE_DEEP_ENTITY in DPC_EXT; read with io_data_provider->read_entry_data into the deep structure; call one BAPI for header + items; return the deep entity.
━━━━━━━━━━━━━━━━━━━━
▶ 4. $BATCH
- One HTTP POST to /$batch containing several requests.
- GET requests outside changesets; modifying requests inside changesets (all-or-nothing).
- CHANGESET_BEGIN / CHANGESET_END can be redefined; for deferred processing use CHANGESET_PROCESS.
- Outside $batch you commit after a successful BAPI yourself; inside a changeset leave the commit to the framework.
━━━━━━━━━━━━━━━━━━━━
▶ 5. PERFORMANCE
- Avoid N+1 reads for $expand on lists.
- Read items for all parents with one SELECT (FOR ALL ENTRIES or range).
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- Add entity SalesOrderItem, association and navigation ToItems; regenerate.
- Implement SALESORDERITEMSET_GET_ENTITYSET reading items for the source key.
- Test /SalesOrderSet('…')/ToItems and ?$expand=ToItems.
- Define the deep type in MPC_EXT and implement CREATE_DEEP_ENTITY.
- POST a header with two items in the Gateway client.
✅ CHECKPOINT: you can serve navigation, $expand and deep insert, and explain changeset commit rules.
Code example
* ===== STEP 1: MPC_EXT - deep type for deep insert =====CLASS zcl_zsales_mpc_ext DEFINITION PUBLIC INHERITING FROM zcl_zsales_mpc CREATE PUBLIC. PUBLIC SECTION. TYPES: BEGIN OF ty_deep_so. INCLUDE TYPE zcl_zsales_mpc=>ts_salesorder. TYPES: toitems TYPE zcl_zsales_mpc=>tt_salesorderitem, "= navigation name END OF ty_deep_so.ENDCLASS. * ===== STEP 2: DPC_EXT - navigation read (/SalesOrderSet('4711')/ToItems) =====METHOD salesorderitemset_get_entityset. DATA: ls_key TYPE /iwbep/s_mgw_name_value_pair, lv_vbeln TYPE vbeln_va. READ TABLE it_key_tab INTO ls_key WITH KEY name = 'SalesOrder'. IF sy-subrc = 0. lv_vbeln = ls_key-value. SELECT vbeln AS salesorder posnr AS salesorderitem matnr AS material FROM vbap INTO CORRESPONDING FIELDS OF TABLE et_entityset WHERE vbeln = lv_vbeln. ENDIF.ENDMETHOD. * ===== STEP 3: DPC_EXT - deep insert (POST header with ToItems) =====METHOD /iwbep/if_mgw_appl_srv_runtime~create_deep_entity. DATA: ls_deep TYPE zcl_zsales_mpc_ext=>ty_deep_so, ls_item TYPE zcl_zsales_mpc=>ts_salesorderitem, lt_return TYPE STANDARD TABLE OF bapiret2, lo_msg TYPE REF TO /iwbep/if_message_container. io_data_provider->read_entry_data( IMPORTING es_data = ls_deep ). LOOP AT ls_deep-toitems INTO ls_item.* fill BAPI item tables from ls_item ... ENDLOOP. * CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDAT2' ... TABLES return = lt_return. READ TABLE lt_return TRANSPORTING NO FIELDS WITH KEY type = 'E'. IF sy-subrc = 0. lo_msg = mo_context->get_message_container( ). lo_msg->add_messages_from_bapi( it_bapi_messages = lt_return ). RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception EXPORTING message_container = lo_msg. ENDIF.* Outside $batch: BAPI_TRANSACTION_COMMIT here. Inside a changeset: no commit. copy_data_to_ref( EXPORTING is_data = ls_deep CHANGING cr_data = er_deep_entity ).ENDMETHOD. * ===== STEP 4: test =====* GET /SalesOrderSet('0000004711')?$expand=ToItems* POST /SalesOrderSet body: {"Customer":"1000","ToItems":[{"Material":"M-01"}]}Real project scenario
A Fiori order list used $expand=ToItems on 200 orders. The service had no expanded-read implementation, so the framework called the item GET_ENTITYSET 200 times; the list took 14 seconds.
The developer redefined GET_EXPANDED_ENTITYSET: one SELECT for headers, one SELECT for all their items, then built the deep result and reported ToItems in et_expanded_tech_clauses. Load time dropped below one second. A later deep-insert bug — orders committed although one item failed — was fixed by removing a COMMIT WORK inside the changeset and letting the framework handle it.
Common mistakes
- Deep type component named differently from the navigation property.
- Relying on default $expand for lists (N+1 reads).
- Forgetting et_expanded_tech_clauses in expanded reads, so the framework expands again.
- COMMIT WORK inside a changeset.
- Not setting referential constraints in the association.
Best practices
- Name deep components exactly like navigation properties.
- Implement expanded reads for list $expand.
- One SELECT for all children, not one per parent.
- Keep changesets small; leave commit to the framework.
- Return BAPI errors via the message container (Part 9).
Interview angle
Questions: "How does $expand work in SEGW?", "How do you implement deep insert?", "What is a changeset in $batch?"
Strong answer: navigation properties from associations; default $expand calls child GET_ENTITYSET per parent, optimized via GET_EXPANDED_ENTITY/ENTITYSET with deep structure and et_expanded_tech_clauses. Deep insert: deep type in MPC_EXT named like the navigation, CREATE_DEEP_ENTITY with read_entry_data. $batch bundles requests; changesets are all-or-nothing with framework-controlled commit, so no COMMIT WORK inside.
Practice this lesson
Quick check
1. A Fiori list uses $expand=ToItems on 200 orders and is slow. The DPC_EXT has no expanded-read implementation. What happens?
2. $batch is used to:
3. Deep insert is:
Interview questions
1. A Fiori order-entry app makes 12 sequential calls to create one order (header, then each item, then texts). Saves are slow and sometimes leave half-created orders. How would you redesign the service?
Answer it in your own words first, then compare.
2. What is a deep insert?
Answer it in your own words first, then compare.
Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.
More lessons in this topic
- OData Gateway Part 1: OData Basics and Your First SEGW Service
- OData Gateway Part 10: Debugging OData Services
- OData Gateway Part 2: SEGW vs RAP — Two Ways to Build OData
- OData Gateway Part 4: Gateway Architecture — Hub vs Embedded
- OData Gateway Part 5: Service Registration, $metadata and Gateway Client Testing
- OData Gateway Part 6: GET_ENTITY and Key Handling
- OData Gateway Part 7: GET_ENTITYSET with Filters, Paging and Sorting
- OData Gateway Part 8: CREATE_ENTITY, UPDATE_ENTITY and DELETE_ENTITY