Skip to content

Creating and Deleting Entities

You can execute POST requests to create entities locally.

Understanding Keys

Keys or Key Values

In many cases, the back-end OData service generates key values for key properties when a new entity is created (key of an entity may consist of multiple properties). This implies that an entity created locally (offline) may not have key values until an upload and a subsequent download is performed. Although key values may be assigned when the entity is created locally, the values may be omitted or overwritten by the back-end OData service. Therefore, key values assigned locally cannot be treated as permanent.

Think of relational databases, which many OData services are built on. Key properties may be implemented as auto-increment columns in tables. If an entity is not assigned a key when being created locally, when the entity is created on the back end, a table will automatically assign a new number if the key property is implemented as an auto-increment integer column. On the other hand, a key assigned locally can be overwritten by an automatically assigned number in the table.

Internally, the offline store uses a separate entity ID as the internal key for an entity created locally. The entity can be uniquely identified by the entity ID. If all key properties of an entity are non-null (all key properties are supplied values), then the entity can be uniquely identified within its entity set using its key values. See below for recommendations regarding use of readLink and editLink constructed with the entity ID.

Uniqueness of Keys

When you create a new entity locally, the key values can be partially or wholly available locally if the application specifies any of them. The back-end OData service MAY change them even if the application specifies them. An entity created locally that has not been uploaded and downloaded can always be identified by its entity ID which contains a generated value. The offline store only enforces uniqueness for keys of new entities if all the key properties are specified and are all non-null. If even a single key property is null/unspecified, offline store does not check for uniqueness. As a result, the developer has two choices regarding assignment of key properties for a new entity:

  1. (Recommended) Leave all or some of the key properties null and allow the back-end OData service to generate the key.
  2. Set the key properties to values that have no chance of conflicting with existing and new entities from the back-end OData service.

    Regardless of whether or not the application specifies key properties when creating new entities, it should never attempt to construct a URL using key properties to identify an entity (either to re-read the entity or to make modifications). The application should always rely on the URLs obtained from the readLink and editLink properties of the entity when rereading or modifying it. The URLs can uniquely identify the entity. Following this approach ensures that an application does not have to have separate code to deal with rereading or modifying locally created entities and those downloaded from the back-end OData service.

Create Entity without Setting Key Property Values

The sample below calls the constructor without setting default property values. That means, the new event does not have an ID when being created locally. You can create as many as you want events locally in the same way without getting the duplicate key exception. The back end will generate IDs for all events.

// Declare a new Event entity without setting properties to default values by passing false to the constructor.
// The entity key, eventID, will be unset
Event event = new Event(false);

// Set property values
event.setCountry("USA");
event.setName("SAP Summit");

// Create a local Event entity
eventService.createEntity(event);
/// Declare a new Event entity without setting properties to default values by passing false to the constructor.
/// The entity key, eventID, will be unset
let event = Event(withDefaults: false)

/// Set property values
event.country = "USA"
event.name = "SAP Summit"

/// Create a local Event entity
try eventService.createEntity(event)

In the sample below, if you call a generated proxy class constructor without any parameters, the event you create will have its properties set to their respective default values. For example, if the key is an integer, the key property will have 0 as its value. Line 9 unsets ID of the event, giving the back end an opportunity to generate the real ID.

// Assign default property values at proxy construction
Event event = new Event();

// Set property values
event.setCountry("USA");
event.setName("SAP Summit");

// Unset entity key explicitly
event.unsetDataValue(Event.eventID);

// Create a local Event entity
eventService.createEntity(event);
/// Assign default property values at proxy class instance construction
let event = Event()

/// Set property values
event.country = "USA"
event.name = "SAP Summit"

/// Unset entity key explicitly
event.unsetDataValue(for: Event.eventID)

/// Create a local Event entity
try eventService.createEntity(event)

If line 9 (calling the unsetDataValue() method) was not present, the new event would have 0 as its ID. Attempting to create a second event constructed in the same way without setting the key property will cause a duplicate key exception as both the two events would have a key property of integer type 0 as ID. Two events with the same ID is not allowed.

To access a newly created entity without key values, retrieve the readLink (read URL) from the entity after the createEntity() method has been performed. The readLink construction uses the locally generated entity ID that uniquely identifies the entity.

// Declare a new Track entity without setting properties to default values by passing false to the constructor.
// The entity key, TrackID, will be unset.
Track track = new Track(false);

// Set required property values
track.setName("Keynote");

// Create a local Track entity
eventService.createEntity(track);

// readLink is available after createEntity() call
String readLink = track.getReadLink();
...

// Use loadEntity() to read back via readLink later
// It is important to declare a new entity without the entity key being set to default value
Track readBackTrack = new Track(false);
readBackTrack.setReadLink(readLink);
eventService.loadEntity(readBackTrack);
/// Not to set default value at proxy construction
let track = Track(withDefaults: false)

/// Set property values
track.name = "Keynote"

/// Create a local Track entity
try eventService.createEntity(track)

/// readLink is available after createEntity() call
let readLink = track.readLink
...

/// Use loadEntity() to read back the entity via readLink later.
/// It is important to declare a new entity without the entity key
/// being set to default value
let readBackTrack = Track(withDefaults: false)
readBackTrack.readLink = readLink
try eventService.loadEntity(readBackTrack)

If you need to apply other query options, for example, select on certain properties or expand on others, create a new DataQuery with appropriate characteristics and use as the second parameter of the loadEntity() method.

Alternatively, you can use the withURL() method, but additional query options require you to directly specify the OData request.

DataQuery query = new DataQuery().withURL(readLink);
Track track = eventService.getTrack(query);
let query = DataQuery().withURL(readLink)
let track = try eventService.fetchTrack(matching: query)

An entity that is downloaded has its readLink constructed using the actual key from the OData service. In addition, the value derived from the actual key replaces the entity ID generated locally. However, the readLink that is retrieved immediately after createEntity(), with a generated entity ID is still usable. Maintaining the entity's ID mapping information helps to simply readLink usage and to avoid a dangling reference. The advantage of readLink is its usage regardless whether the entity has been created, uploaded, or downloaded.

Deleting Entities

You can delete an entity, no matter it is downloaded from the OData service or created locally and not uploaded yet.

You can directly delete an entity, or use editLink of an entity for deletion.

// Get editLink of the entity to delete
String editLink = ...;

// Delete the entity using the editLink
Track trackToDelete = new Track(false);
trackToDelete.setEditLink(editLink);
eventService.deleteEntity(trackToDelete);
/// Get editLink of an entity
let editLink = track.editLink

/// Delete an entity with the editLink
let trackToDelete = Track(withDefaults: false)
trackToDelete.editLink = editLink
try eventService.deleteEntity(trackToDelete)

A download may take place before any upload. An entity deleted locally remains in deleted mode even if the download informs that the entity has been deleted from the OData service. This means a queued delete request will cause a failure when it is uploaded. A second upload and download will resolve this issue.

You can use either readLink or editLink to identify an entity for modification. In most cases, readLink and editLink have the same URL, but it's recommended that you adhere to their intended usage when leveraging them to read, update or delete.


Last update: June 7, 2022