Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
Showing results for 
Search instead for 
Did you mean: 
Product and Topic Expert
Product and Topic Expert
This tutorial describes

How to implement
Deep Insert
SAP Cloud Application Programming Model
(Part 2: using UUID)

Sample Project files
Part 1: Intro
Part 3: Multi-Level
Part 4: Consume Remote Service
Part 5: Remote Service with SDK

This blog is required to cover a case which I guess is relevant for most CAP users:
In CDS models, the data type frequently used for key fields is UUID:
key companyId : UUID;

The benefit is that the value can be generated by the framework.
It affects the CREATE operation, where the user doesn't send a value for key property
As such, it also affects the DEEP INSERT

Let's view it in detail

Note that this blog builds upon the previous blog where we created a project to learn how to implement deep insert

New Requirements

In the previous blog we implemented deep insert for a model without generated key values
Let’s have a look at our request payload:

"companyId": 3,
"companyName": "BeerShop",
"linkToContact_contactId": "ContactForBeerShop",
"linkToContact": {
"contactId": "ContactForBeerShop",
"contactName": "Peter",
"contactPhone": 9876543

We can see that we’ve passed each and every property
But in reality, we don’t want to pass ID values, we want it to be automatically generated
So we would remove the key fields from payload:

"companyName": "BeerShop",
"linkToContact_contactId": "ContactForBeerShop",
"linkToContact": {
"contactName": "Peter",
"contactPhone": 9876543

But in above payload, there’s a problem: we don’t know the foreign key value, because it should be generated:
"linkToContact_contactId": "Which contactId?",
No, it must be filled by the server
So we remove that field from payload:

"companyName": "BeerShop",
"linkToContact": {
"contactName": "Peter",
"contactPhone": 9876543

This is how it should look like:
User doesn't care about IDs.
Generating and assigning is done by the service

But how to realize it?
Well, remember: the service - that's us

We will need some code.
And a little change in CDS model

Handling UUID

You may create a new project or just apply 2 little changes to your existing project:
Change the data type assigned to the key properties to UUID, for both entities:
namespace com.relation;

entity CompanyEntity {
key companyId : UUID;
companyName : String;
linkToContact : Association to ContactEntity;

entity ContactEntity {
key contactId : UUID;
contactName : String;
contactPhone : Integer;

Apart from these 2 little changes, all the rest of the project of previous blog remains the same.
And of course, a couple of lines which we have to add to the java code

After re-deploying the mtar, containing the modeified CDS model, your existing data will disappear, due to change of table meatadata


We have to recognize that the requirements mentioned above require additional effort by us.
Changing the data type to UUID is not enough.

Key values for UUID are usually generated by the FWK, but not for nested entities in a deep insert

Second requirement:The value of foreign-key element.
If the FWK doesn't generate a UUID, then obviously it cannot be automatically assigned to the foreign-key field

Both tasks have to be taken care in our custom code


As pointed out, in addition to the implementation we did above, we need to manually implement 2 little tasks:

1. Generate key value for inline entity

As we know, if we specify UUID as data type for a key field in CDS, then the value will be generated by the FWK
For the user of the OData service, this has the advantage:
When creating an entry with POST request, he doesn’t need to send the key property in the request body

BUT: this works only for normal CREATE requests.
In case of deep insert, only the key value for the parent entity (CompanyEntity) will be generated on the fly.
But not for the associated entities (ContactEntity).
Because the FWK doesn’t know which is the key property.
Because it is done before our implementation code is reached

We have to manually generate a UUID for the associated entities in the deep insert
UUID contactGuid = UUID.randomUUID();  
Map<String, Object> inlineContactMap = (Map<String, Object>)mapForCreation
inlineContactMap.put("contactId", contactGuid);

In this snippet, we can see:
We generate a UUID (in OData, the type is Edm.Guid) manually
To set the UUID as key value for the nested ContactEntity, we have to fetch the corresponding map
We have a map which represents the payload of the POST request. It contains the parent entity plus nested child entities
The map contains a key which is a navigation property ("linkToContact")
The value is a map, it is the data for the nested entity (ContactEntity). This is what will be created inline, the deep insert.
In this nested map, we have to set the value (the UUID) for the key field (contactId)
If the end-user has passed a value, it will be overridden, which is the desired behavior.

BTW, the above code has to be surrounded by a little check such that it runs only in case of deep insert.

2. foreign-key value

We’ve generated a GUID and set it as key value for the nested entity.
This is the target of the association.
So at this point in time, the CREATE operation on the database will create 2 new rows in the 2 tables
But the new company entry needs to know the ID of the contact, otherwise the navigation wont work.
As such, now we have to set the same value also for the foreign-key field of the parent entity

In our CDS model, we have a managed Association from CompanyEntity to ContactEntity
“Managed” means, that CDS generates a property into the CompanyEntiy which carries the foreign-key for the associated ContactEntity
Means, a Company knows which Contact of the list of Contacts belongs to the Company
This generated property is visible in the edmx.
As such, when generating a guid for the Contact, we have to set this GUID as value for the foreign-key property of the CompanyEntity
This is done in the following line:
mapForCreation.put("linkToContact_contactId", contactGuid);

We have a map which contains all the data which will be created on the database.
In this map we set the value for the foreign-key property ("linkToContact_contactId")

The name of the foreign-key property is generated by CDS, so it has to be searched in the (generated) edmx or CSN file (in folder src/main/resources/edmx)

See appendix for the full code

Test it

After deployment, try the deep insert with this payload:

Content-Type: application/json
Request body

It should work without error and both entities should be created and the navigation (or $expand) should work fine


In addition to what we summarized in the previous blog, we can summarize the following:
If the deep insert includes child entities which have UUID as datatype for the key element, then it is necessary to write additional code in the custom implementation of CREATE.
1. the value has to be created and assigned to key of nested child
2. the same value has to be assigned to the foreign-key element of the parent


Appendix: Sample Project files

namespace com.relation;

entity CompanyEntity {
key companyId : UUID;
companyName : String;
linkToContact : Association to ContactEntity;

entity ContactEntity {
key contactId : UUID;
contactName : String;
contactPhone : Integer;

using com.relation from '../db/data-model';

service RelationService {
entity CompanyEntity as projection on relation.CompanyEntity;
entity ContactEntity as projection on relation.ContactEntity;
package com.example.deep;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServiceImplementation {
private static Logger logger = LoggerFactory.getLogger(ServiceImplementation.class);

@Create(entity = "CompanyEntity", serviceName = "RelationService")
public CreateResponse createCompany(CreateRequest createRequest, ExtensionHelper extensionHelper) throws DatasourceException{
// 1) retrieve the request payload, the data to create in backend
Map<String, Object> mapForCreation = createRequest.getData().asMap();

// special handling required in case of UUID: FWK cannot generate it for inline entity
Map<String, Object> inlineContactMap = (Map<String, Object>)mapForCreation.get("linkToContact");
// check if request is deep insert
if(inlineContactMap != null) {
// manually generate Guid for inline-entity-key-field (Contacts) and foreign-key-field (Companies)
UUID contactGuid = UUID.randomUUID();
inlineContactMap.put("contactId", contactGuid); // fill the key field of inline entity (Contacts)
mapForCreation.put("linkToContact_contactId", contactGuid);// fill the forein-key field of "Companies" entity

// 2) our actual task is: specify key field for navigation entity
//Compose the map of key list for all entities of the deep insert
Map<String, List<String>> keyMap = new HashMap<String, List<String>>();
// the key map for the parent entity: Companies. Here, the key field is "companyId"
keyMap.put("CompanyEntity", Collections.singletonList("companyId"));
// here we assign the key field (contactId) of navigation target entity (Contacts) to the navigationProperty name (contact)
keyMap.put("linkToContact", Collections.singletonList("contactId"));

// 3) send data to database, including the info about keys
EntityData entityDataToCreate = EntityData.createFromDeepMap(mapForCreation, keyMap, "RelationService.CompanyEntity");
// execute it in database
EntityData result = extensionHelper.getHandler().executeInsertWithAssociations(entityDataToCreate, true);// true to return created entity
return CreateResponse.setSuccess().setData(result).response();

_schema-version: 2.0.0
ID: DeepInsertDemo
version: 1.0.0
- name: DeepInsertDemo-db
type: hdb
path: db
memory: 256M
disk-quota: 256M
- name: DeepInsertDemo-db-hdi-container
- name: DeepInsertDemo-srv
type: java
path: srv
memory: 990M
- name: srv_api
url: ${default-url}
- name: DeepInsertDemo-db-hdi-container
{"service_name_for_DefaultDB" : "~{hdi-container-name}"}]'
- name: DeepInsertDemo-uaa
- name: DeepInsertDemo-db-hdi-container
hdi-container-name: ${service-name}
- name: DeepInsertDemo-uaa
type: org.cloudfoundry.managed-service
service-plan: application
service: xsuaa
xsappname: DeepInsertDemo-${space}
tenant-mode: dedicated