OData V2 Part 2: Deep Insert, Batch and Navigation Design
Design OData V2 navigation and write operations: associations and navigation properties, $expand, deep insert of header and items, $batch with change sets, and the implementation side in SEGW (CREATE DEEP ENTITY, changeset methods).
Explanation
📌 KEY POINTS
✅ Navigation properties (from associations) let clients read related data: /Header('1')/ToItems or $expand.
✅ Deep insert = POST a header with its items in one request; SEGW implements CREATE_DEEP_ENTITY.
✅ $batch (multipart) with changesets groups writes into one logical unit of work.
⚠️ In SEGW you implement all of this yourself — in RAP V2 bindings the framework does it.
━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE
Part 1 covered reading V2 services. This part covers relationships and complex writes — where many V2 projects struggle.
━━━━━━━━━━━━━━━━━━━━
▶ 1. NAVIGATION DESIGN
- In the model: Association (Header 1 : n Item) + NavigationProperty (ToItems on Header, ToHeader on Item).
- Clients: /PurchaseRequestSet('4711')/ToItems or ?$expand=ToItems.
- Design rule: model navigation the UI actually needs; avoid huge $expand chains in lists.
- In SEGW, $expand is handled generically (calls item GET_ENTITYSET per header) unless you implement GET_EXPANDED_ENTITYSET for performance.
━━━━━━━━━━━━━━━━━━━━
▶ 2. DEEP INSERT
- Client sends one POST with nested items: { "RequestID": "", "Description": "…", "ToItems": [ { … }, { … } ] }.
- Server (SEGW): redefine /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CREATE_DEEP_ENTITY in the DPC_EXT class; read the nested data with io_data_provider->read_entry_data into a deep structure; create header and items in one call to your business logic; return the created structure.
- RAP V2 binding: deep insert works via the composition automatically.\n\n━━━━━━━━━━━━━━━━━━━━\n▶ 3. $BATCH AND CHANGESETS\n• V2 $batch is a multipart/mixed request: read requests outside changesets, write requests inside changesets.
- A changeset should be processed as one LUW: all or nothing.
- SEGW: redefine CHANGESET_BEGIN (accept the changeset, e.g. set cv_defer_mode for deferred processing) and CHANGESET_END (commit). Without this, each operation may commit separately.
- UI5 V2 model sends $batch by default (useBatch: true) and groups changes with deferred groups.
━━━━━━━━━━━━━━━━━━━━
▶ 4. COMMITS IN SEGW
The gateway framework commits at the end of a successful request/changeset. Don't call COMMIT WORK inside CRUD methods; raise /IWBEP/CX_MGW_BUSI_EXCEPTION on errors so the framework rolls back and returns messages.
━━━━━━━━━━━━━━━━━━━━
▶ 5. ETAGS IN V2
Entity types can have an ETag property (e.g. timestamp); clients send If-Match; the framework compares for PUT/MERGE/DELETE.
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- Check the Header–Item association and navigation in $metadata.
- Read with $expand=ToItems.
- Send a deep insert (code example).
- For SEGW services: implement CREATE_DEEP_ENTITY and changeset handling as shown.
✅ CHECKPOINT: you can design and implement V2 navigation and complex writes.
Code example
### ===== Deep insert (client) =====POST /sap/opu/odata/sap/ZPURCHREQ_SRV/PurchaseRequestSetContent-Type: application/jsonX-CSRF-Token: <token> { "Description": "Laptops", "Currency": "EUR", "ToItems": [ { "ItemNo": "0010", "Quantity": "3", "Price": "1200.00" }, { "ItemNo": "0020", "Quantity": "3", "Price": "90.00" } ] } " ===== SEGW: CREATE_DEEP_ENTITY in ZCL_ZPURCHREQ_DPC_EXT =====METHOD /iwbep/if_mgw_appl_srv_runtime~create_deep_entity. TYPES: BEGIN OF ty_deep. INCLUDE TYPE zcl_zpurchreq_mpc=>ts_purchaserequest. TYPES: toitems TYPE zcl_zpurchreq_mpc=>tt_purchaserequestitem, END OF ty_deep. DATA deep TYPE ty_deep. io_data_provider->read_entry_data( IMPORTING es_data = deep ). " header + items " one call into business logic (no COMMIT here - framework commits) DATA(result) = zcl_pr_service=>create_request( header = CORRESPONDING #( deep ) items = deep-toitems ). IF result-has_error = abap_true. RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception EXPORTING textid = /iwbep/cx_mgw_busi_exception=>business_error message = result-message. ENDIF. deep-requestid = result-request_id. copy_data_to_ref( EXPORTING is_data = deep CHANGING cr_data = er_deep_entity ).ENDMETHOD. " ===== SEGW: changeset as one LUW =====METHOD /iwbep/if_mgw_appl_srv_runtime~changeset_begin. " accept all operations of the changeset (process as a unit) cv_defer_mode = abap_false.ENDMETHOD.METHOD /iwbep/if_mgw_appl_srv_runtime~changeset_end. COMMIT WORK. " one commit for the whole changesetENDMETHOD. ### ===== $batch (multipart) outline =====# --batch_1# Content-Type: multipart/mixed; boundary=changeset_1# --changeset_1 POST PurchaseRequestSet ...# --changeset_1 MERGE PurchaseRequestSet('4711') ...# --changeset_1--# --batch_1 GET PurchaseRequestSet('4711')?$expand=ToItems# --batch_1--Real project scenario
A SEGW service accepted header and items in separate calls from a UI5 app. When item creation failed, headers without items remained, and users created them again — leading to duplicates and manual cleanup.
The team implemented CREATE_DEEP_ENTITY with a single business-logic call and proper exception handling, so header and items were created together or not at all. For multi-step edits, changeset handling ensured one commit per changeset. Orphan headers disappeared.
Common mistakes
- Creating header and items in separate requests without atomicity.
- COMMIT WORK inside CRUD methods.
- Not raising business exceptions, so errors return as success.
- Relying on generic $expand for large lists (N+1 calls).
- Ignoring changeset handling in SEGW.
- Over-modeling navigation the UI never uses.
Best practices
- Use deep insert for header-item creation.
- Let the framework commit; raise /IWBEP/CX_MGW_BUSI_EXCEPTION on errors.
- Handle changesets as one LUW.
- Implement expanded entity sets for performance-critical $expand.
- Prefer RAP (even with V2 binding) for new services — the framework handles this.
Interview angle
Questions: "How do you implement deep insert in SEGW?", "What is a changeset?", "How is $expand handled in Gateway?"
Strong answer: deep insert posts header with nested items; SEGW redefines CREATE_DEEP_ENTITY, reads the deep structure via io_data_provider->read_entry_data, calls business logic once and returns er_deep_entity. $batch groups requests; changesets are write units processed as one LUW (CHANGESET_BEGIN/END). $expand is generic unless GET_EXPANDED_ENTITYSET is implemented. RAP V2 bindings do this automatically.
Practice this lesson
Quick check
1. Header and items must be created together or not at all in a SEGW service. What should be implemented?
2. In a V2 $batch request, where must modifying operations be placed to run as one unit?
3. Generic $expand of items for 200 headers causes 200 item selects. What improves it?
Interview questions
1. A UI5 app creates a header and then its items in separate calls; failures leave orphan headers. How do you fix it?
Answer it in your own words first, then compare.
2. How do you implement deep insert and changesets in a SEGW service?
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.