event kotlin odata |
https://help.sap.com/doc/f53c64b93e5140918d676b927a3cd65b/Cloud/en-US/docs-en/guides/features/backend-connectivity/android/using-o |
温暖的海龟
10 月前 |
This section covers how to use the OData API to perform common queries, create/update/delete (CUD) operations, and service operations. Each section includes code for using the generated proxy classes with Dynamic API, followed by another code snippet using Dynamic API alone.
An instance of the
DataService
class is required to interact with the OData endpoint that it represents. It relies on parsed metadata from the service document provided by the OData endpoint. For proxy classes, a generated service class that is a subclass of
DataService
should be used.
Every query requires the construction of a corresponding
DataQuery
, passed as a parameter to the
DataService
executeQuery
method. With proxy classes, you can use the getter methods for the entity set provided by the generated service class. Internally, those getter methods make use of
DataQuery
and
executeQuery
in their implementation. It is therefore important to have a good understanding of the
DataQuery
class to query an OData service provider.
The first couple of code samples include the lookup of the entity set, entity type, and property required for parameters when used without proxy classes. Subsequent samples skip these lookups to make the code easier to read. The following variable naming convention is used for entity set, entity type, and properties.
eventType
eventSet
eventEventIDProp
// OData Query: /Events
// Proxy Class
// Use generated service class getter method for collection/entity set
// Implementation code creates a DataQuery to indicate the entity set to be queried
List<Event> events = eventService.getEvents();
for (Event event: events) {
String eventName = event.getName();
// Get the start date time of the event
LocalDateTime startDateTime = event.getStartDateTime();
// Dynamic API
// Use DataService to look up the entity set with the name of the entity set element from the service document
// Get the entity set
EntitySet eventSet = dataService.getEntitySet("Events");
// Get the type of the entity
EntityType eventType = eventSet.getEntityType();
// Use the from method of DataQuery to specify the entity set to retrieve from
DataQuery query = new DataQuery().from(eventSet);
EntityValueList events = dataService.executeQuery(query).getEntityList();
// Get event name value from result
Property eventNameProp = eventType.getProperty("Name");
Property eventStartDateTimeProp = eventType.getProperty("StartDateTime");
for (EntityValue event : events) {
String eventName = eventNameProp.getString(event);
LocalDateTime startDateTime =
LocalDateTime.castRequired(eventStartDateTimeProp.getValue(event));
// OData Query: /Events
// Proxy Class
// Use generated service class getter method for collection/entity set
// Implementation code creates a DataQuery to indicate the entity set to be queried
val events = eventService.events
for (event in events) {
val eventName = event.name
// Get the start date time of the event
val startDateTime = event.startDateTime
// Dynamic API
// Use DataService to look up the entity set with the name of the entity set element from the service document
// Get the entity set
val eventSet = dataService.getEntitySet("Events")
// Get the type of the entity
val eventType = eventSet.entityType
// Use the from method of DataQuery to specify the entity set to retrieve from
val query = DataQuery().from(eventSet)
val events = dataService.executeQuery(query).entityList
// Get event name value from result
val eventNameProp = eventType.getProperty("Name")
val eventStartDateTimeProp = eventType.getProperty("StartDateTime")
for (event in events) {
val eventName = eventNameProp.getString(event)
val startDateTime =
LocalDateTime.castRequired(eventStartDateTimeProp.getValue(event))
With the generated service class, you don't have to specify the entity set from which retrieval is to take place as it is already in its implementation. In addition, a native list of strongly typed class instead of a generic EntityValueList
is returned.
Client-Driven Paging¶
The client determines the size of a page and uses the skip
and top
query options to request a specific page. This is known as client-driven paging.
// OData Query: /Events?$skip=100&$top=50
// Page size = 50, reading third page
// Proxy Class
// Use a query for getEvents
DataQuery query = new DataQuery().skip(100).top(50);
List<Event> events = eventService.getEvents(query);
// Dynamic API
DataQuery query = new DataQuery().from(eventSet).skip(100).top(50);
EntityValueList events = dataService.executeQuery(query).getEntityList();
// OData Query: /Events?$skip=100&$top=50
// Page size = 50, reading third page
// Proxy Class
// Use a query for getEvents
val query = DataQuery().skip(100).top(50)
val events = eventService.getEvents(query)
// Dynamic API
val query = DataQuery().from(eventSet).skip(100).top(50)
val events = dataService.executeQuery(query).entityList
Please be aware that if the membership of the collection is changing, paging may return an entity twice or not at all. This is how paging works against a changing collection.
InlineCount
¶
InlineCount
is a system query option that indicates that the response to the request includes a count of the number of entities satisfying the condition in the filter query option. If no filter query option is specified, it is assumed to be the entire collection. InlineCount
is particular useful for client-driven paging to determine the total number of pages of entities satisfying the query, allowing it to prepare UI for a user to retrieve any one of the pages.
// OData: /Events?$inlinecount=allpages&$top=50&$skip=0
// Proxy Class
// When using executeQuery instead of getEvents or getEvent we need to
// specify which entity set the query is running against
DataQuery query = new DataQuery()
.from(EntitySets.events)
.skip(0)
.top(50)
.inlineCount();
QueryResult result = eventService.executeQuery(query);
long count = result.getInlineCount();
// a list of 50 events will be returned
List<Event> events = Event.list(result.getEntityList());
// Dynamic API
DataQuery query = new DataQuery()
.from(eventSet)
.skip(0)
.top(50)
.inlineCount();
QueryResult result = dataService.executeQuery(query);
long count = result.getInlineCount();
// a list of 50 events will be returned
EntityValueList events = result.getEntityList();
// OData: /Events?$inlinecount=allpages&$top=50&$skip=0
// Proxy Class
// When using executeQuery instead of getEvents or getEvent we need to
// specify which entity set the query is running against
val query = DataQuery()
.from(EntitySets.events)
.skip(0)
.top(50)
.inlineCount()
val result = eventService.executeQuery(query)
val count = result.inlineCount
// a list of 50 events will be returned
val events = Event.list(result.entityList)
// Dynamic API
val query = DataQuery()
.from(eventSet)
.skip(0)
.top(50)
.inlineCount()
val result = dataService.executeQuery(query)
val count = result.inlineCount
// a list of 50 events will be returned
val events = result.entityList
Server-Driven Paging¶
In server-driven paging, the server determines how much to send back for a request. It is possible to influence the server by specifying a page size preference. However, it is up to the server whether it will honor the setting. At the end of the server response is a nextLink
URL for use by the client to fetch the next page of entities.
It is possible to get a count of the total number of entities via the InlineCount
query option. However, the client can only read the next page via nextLink
.
// Proxy Class
DataQuery pagedQuery = new DataQuery()
.from(EntitySets.events)
.page(10);
QueryResult result = eventService.executeQuery(pagedQuery);
List<Event> events = Event.list(result.getEntityList());
// Get the query based on the nextLink URI returned by the server
pagedQuery = result.getNextQuery();
// Ready to query the server again for the next page of entities
// Dynamic API
DataQuery pagedQuery = new DataQuery()
.from(eventSet)
.page(10);
QueryResult result = dataService.executeQuery(pagedQuery);
EntityValueList events = result.getEntityList();
// Get the query based on the nextLink URI returned by the server
pagedQuery = result.getNextQuery();
// Ready to query the server again for the next page of entities
// Proxy Class
var pagedQuery = DataQuery()
.from(EntitySets.events)
.page(10)
val result = eventService.executeQuery(pagedQuery)
val events = Event.list(result.entityList;
// Get the query based on the nextLink URI returned by the server
pagedQuery = result.nextQuery
// Ready to query the server again for the next page of entities
// Dynamic API
var pagedQuery = new DataQuery()
.from(eventSet)
.page(10)
val result = dataService.executeQuery(pagedQuery)
val events = result.entityList
// Get the query based on the nextLink URI returned by the server
pagedQuery = result.nextQuery
// Ready to query the server again for the next page of entities
Single Entity with Entity Key¶
The entity key is required to access a particular instance of the entity set. The getter method takes a DataQuery
constructed with the entity key as the parameter.
// OData Query: /Events(1000L)
// Proxy Class
DataQuery query = new DataQuery().withKey(Event.key(1000L));
Event event = eventService.getEvent(query);
// Dynamic API
DataValue keyValue = LongValue.of(1000L);
// Note: composite key can be created using multiple with methods
// Assume Session requires a composite key of EventID and SessionID
// new EntityKey().with("EventID", keyValue).with("SessionID", idValue)
EntityKey eventKey = new EntityKey().with("EventID", keyValue);
DataQuery query = new DataQuery().from(eventSet).withKey(eventKey);
EntityValue event = dataService.executeQuery(query).getRequiredEntity();
// OData Query: /Events(1000L)
// Proxy Class
val query = DataQuery().withKey(Event.key(1000L))
val event = eventService.getEvent(query)
// Dynamic API
val keyValue = LongValue.of(1000L)
// Note: composite key can be created using multiple with methods
// Assume Session requires a composite key of EventID and SessionID
// new EntityKey().with("EventID", keyValue).with("SessionID", idValue)
val eventKey = EntityKey().with("EventID", keyValue)
val query = DataQuery().from(eventSet).withKey(eventKey)
val event = dataService.executeQuery(query).requiredEntity
If there is no event with the specified key, an exception will be thrown. This is due to the getRequiredEntity
method used in the implementation of getEvent
in the generated service class. With Dynamic API, it is possible to return null by using getOptionalEntity
.
EntityValue event = dataService.executeQuery(query).getOptionalEntity();
val event = dataService.executeQuery(query).optionalEntity
Filter¶
You can create a filter to retrieve the subset of a collection that satisfies its criteria. An empty collection will be returned if there is no entity within the collection that satisfies the criteria.
The withKey
method of DataQuery
essentially generates a filter using the entity key.
// OData Query: /Events?$filter=Name eq 'SAP Tech Ed 2015'
// Proxy Class
DataQuery query = new DataQuery()
.filter(Event.name.equal("SAP Tech Ed 2015"));
List<Event> events = eventService.getEvents(query);
// Dynamic API
DataQuery query = new DataQuery()
.from(eventSet)
.filter(eventNameProp.equal("SAP Tech Ed 2015"));
EntityValueList events = dataService.executeQuery(query).getEntityList();
// OData Query: /Events?$filter=Name eq 'SAP Tech Ed 2015'
// Proxy Class
val query = DataQuery().filter(Event.name.equal("SAP Tech Ed 2015"))
val events = eventService.getEvents(query)
// Dynamic API
val query = DataQuery()
.from(eventSet)
.filter(eventNameProp.equal("SAP Tech Ed 2015"))
val events = dataService.executeQuery(query).entityList
For complex filtering criteria, consider using the QueryOperator
within the filter.
// Find all sessions with registration >= 90%
// Proxy Class
DataQuery query = new DataQuery().filter(
QueryOperator.multiply(Session.participants, IntValue.of(100))
.divide(Session.maxParticipants)
.greaterEqual(IntValue.of(90)));
List<Session> sessions = eventService.getSessions(query);
// Dynamic API
DataQuery query = new DataQuery()
.from(sessionSet)
.filter(
QueryOperator.multiply(sessionParticipantsProp, IntValue.of(100))
.divide(sessionMaxParticipantsProp)
.greaterEqual(IntValue.of(90))
EntityValueList sessions = dataService.executeQuery(query).getEntityList();
// Find all sessions with registration >= 90%
// Proxy Class
val query = DataQuery().filter(
QueryOperator.multiply(Session.participants, IntValue.of(100))
.divide(Session.maxParticipants)
.greaterEqual(IntValue.of(90)))
val sessions = eventService.getSessions(query)
// Dynamic API
val query = DataQuery()
.from(sessionSet)
.filter(
QueryOperator.multiply(sessionParticipantsProp, IntValue.of(100))
.divide(sessionMaxParticipantsProp)
.greaterEqual(IntValue.of(90))
val sessions = dataService.executeQuery(query).entityList
Count¶
Count the number of entities in a collection, or the number of entities that satisfies a specific query. Note that the list of qualifying events is not returned, only the count.
// OData: /Events/$count
// Proxy Class
DataQuery query = new DataQuery().from(EntitySets.events).count();
long count = eventService.executeQuery(query).getCount();
// Dynamic API
DataQuery query = new DataQuery().from(eventSet).count();
long count = dataService.executeQuery(query).getCount();
// OData: /Events/$count&$filter=Country eq 'USA'
// Proxy Class
DataQuery query = new DataQuery().from(EntitySets.events)
.filter(QueryOperator.equal(Event.country, StringValue.of('USA')))
.count();
long count = eventService.executeQuery(query).getCount();
// Dynamic API
DataQuery query = new DataQuery().from(eventSet)
.filter(QueryOperator.equal(eventCountryProp, StringValue.of('USA')))
.count();
long count = dataService.executeQuery(query).getCount();
// OData: /Events/$count
// Proxy Class
val query = DataQuery().from(EntitySets.events).count()
val count = eventService.executeQuery(query).count
// Dynamic API
val query = DataQuery().from(eventSet).count()
val count = dataService.executeQuery(query).count
// OData: /Events/$count&$filter=Country eq 'USA'
// Proxy Class
val query = DataQuery().from(EntitySets.events)
.filter(QueryOperator.equal(Event.country, StringValue.of('USA')))
.count()
val count = eventService.executeQuery(query).count
// Dynamic API
val query = DataQuery().from(eventSet)
.filter(QueryOperator.equal(eventCountryProp, StringValue.of('USA')))
.count()
val count = dataService.executeQuery(query).count
Expand¶
To expand, specify a navigation property to retrieve associated entities. For example, when we retrieve an event, we can have all the sessions belonging to the event be returned inline.
// OData: /Events?$filter=Name eq 'SAP Tech Ed 2015'&$expand=Sessions
// Proxy Class
DataQuery query = new DataQuery()
.filter(Event.name.equal("SAP Tech Ed 2015"))
.expand(Event.sessions);
List<Event> events = eventService.getEvents(query);
// Dynamic API
Property eventSessionsProp = eventType.getProperty("Sessions");
DataQuery query = new DataQuery().from(eventSet)
.filter(eventNameProp.equal("SAP Tech Ed 2015"))
.expand(eventSessionsProp);
EntityValueList events = dataService.executeQuery(query).getEntityList();
// OData: /Events?$filter=Name eq 'SAP Tech Ed 2015'&$expand=Sessions
// Proxy Class
val query = DataQuery()
.filter(Event.name.equal("SAP Tech Ed 2015"))
.expand(Event.sessions)
val events = eventService.getEvents(query)
// Dynamic API
val eventSessionsProp = eventType.getProperty("Sessions")
val query = DataQuery().from(eventSet)
.filter(eventNameProp.equal("SAP Tech Ed 2015"))
.expand(eventSessionsProp)
val events = dataService.executeQuery(query).entityList
You can expand with multiple properties by passing a comma-separated list of navigation properties to expand or invoking expand multiple times. The following code fragments demonstrate how to do this using proxy classes.
// OData: /Events?$filter=Name eq 'SAP Tech Ed 2015'&$expand=Sessions,Features
// Proxy Class
DataQuery query = new DataQuery()
.filter(Event.name.equal("SAP Tech Ed 2015"))
.expand(Event.sessions, Event.features);
List<Event> events = eventService.getEvents(query);
DataQuery query = new DataQuery()
.filter(Event.name.equal("SAP Tech Ed 2015"))
.expand(Event.sessions)
.expand(Event.features);
List<Event> events = eventService.getEvents(query);
// OData: /Events?$filter=Name eq 'SAP Tech Ed 2015'&$expand=Sessions,Features
// Proxy Class
val query = DataQuery()
.filter(Event.name.equal("SAP Tech Ed 2015"))
.expand(Event.sessions, Event.features)
val events = eventService.getEvents(query)
val query = DataQuery()
.filter(Event.name.equal("SAP Tech Ed 2015"))
.expand(Event.sessions)
.expand(Event.features)
val events = eventService.getEvents(query)
Multi-level expansion is also possible with the use of the expandWithQuery
method. The following example shows how to retrieve an event with sessions returned inline. In addition, the track entity associated with each session instance will also be returned inline.
// OData: /Events(1000L)?$expand=Sessions/Track
// Proxy Class
DataQuery query = new DataQuery()
.withKey(Event.key(1000L))
.expandWithQuery(
Event.sessions,
new DataQuery().expandWithQuery(Session.track, new DataQuery())
List<Event> events = eventService.getEvents(query);
// Dynamic API
EntityKey eventKey = new EntityKey().with("EventID", LongValue.of(1000L));
DataQuery query = new DataQuery()
.withKey(eventKey)
.expandWithQuery(
eventSessionsProp,
new DataQuery().expandWithQuery(sessionTrackProp, new DataQuery())
List<Event> events = eventService.getEvents(query);
// OData: /Events(1000L)?$expand=Sessions/Track
// Proxy Class
val query = DataQuery()
.withKey(Event.key(1000L))
.expandWithQuery(
Event.sessions,
DataQuery().expandWithQuery(Session.track, DataQuery())
val events = eventService.getEvents(query)
// Dynamic API
val eventKey = EntityKey().with("EventID", LongValue.of(1000L))
val query = DataQuery()
.withKey(eventKey)
.expandWithQuery(
eventSessionsProp,
DataQuery().expandWithQuery(sessionTrackProp, DataQuery())
val events = eventService.getEvents(query)
orderBy
¶
Specify the orderBy
system query option to sort the returned set of entities.
// OData: /Events?$orderby=Country asc,Name asc
// Sort by country in ascending order then by name in ascending order
// Proxy Class
DataQuery query = new DataQuery()
.orderBy(Event.country, SortOrder.ASCENDING)
.thenBy(Event.name, SortOrder.ASCENDING);
List<Event> events = eventService.getEvents(query);
// Dynamic API
DataQuery query = new DataQuery()
.from(eventSet)
.orderBy(eventCountryProp, SortOrder.ASCENDING)
.thenBy(eventNameProp, SortOrder.ASCENDING);
EntityValueList events = dataService.executeQuery(query).getEntityList();
// OData: /Events?$orderby=Country asc,Name asc
// Sort by country in ascending order then by name in ascending order
// Proxy Class
val query = DataQuery()
.orderBy(Event.country, SortOrder.ASCENDING)
.thenBy(Event.name, SortOrder.ASCENDING)
val events = eventService.getEvents(query)
// Dynamic API
val query = DataQuery()
.from(eventSet)
.orderBy(eventCountryProp, SortOrder.ASCENDING)
.thenBy(eventNameProp, SortOrder.ASCENDING)
val events = dataService.executeQuery(query).entityList
In some cases, you may want to sort by a property of the entity associated with the navigation property. For example, you may want to list the sessions sorted by the name of their associated track. To do so, create a DataPath
as a parameter for the orderBy
method.
// OData: /Sessions?$filter=EventID eq 1000L&$expand=Track&$orderby=Track/Name
// Proxy Class
// Create the path Track/Name
DataPath trackNamePath = Session.track.path(Track.name);
DataQuery sortQuery = new DataQuery()
.filter(Session.eventID.equal(1000L))
.expand(Session.track)
.orderBy(trackNamePath);
List<Session> sessions = eventService.getSessions(query);
// Dynamic API
// Create the path Track/Name
DataPath trackNamePath = sessionTrackProp.path(trackNameProp);
DataQuery sortQuery = new DataQuery().from(setSession)
.filter(sessionEventIDProp.equal(1000L))
.expand(sessionTrackProp)
.orderBy(trackNamePath);
EntityValueList sessions = dataService.executeQuery(query).getEntityList();
// OData: /Sessions?$filter=EventID eq 1000L&$expand=Track&$orderby=Track/Name
// Proxy Class
// Create the path Track/Name
val trackNamePath = Session.track.path(Track.name)
val sortQuery = DataQuery()
.filter(Session.eventID.equal(1000L))
.expand(Session.track)
.orderBy(trackNamePath)
val sessions = eventService.getSessions(query)
// Dynamic API
// Create the path Track/Name
val trackNamePath = sessionTrackProp.path(trackNameProp)
val sortQuery = DataQuery().from(setSession)
.filter(sessionEventIDProp.equal(1000L))
.expand(sessionTrackProp)
.orderBy(trackNamePath)
val sessions = dataService.executeQuery(query).entityList
Select¶
Use the select
system query option to specify a subset of the structural properties to be returned with the query.
// OData: /Events?$select=Name,EventID,Country
// Proxy Class
DataQuery query = new DataQuery().select(Event.name, Event.eventID, Event.country);
List<Event> events = eventService.getEvents(query);
// Dynamic API
DataQuery query = new DataQuery()
.from(eventSet)
.select(eventNameProp, eventEventIDProp, eventCountryProp);
EntityValueList events = dataService.executeQuery(query).getEntityList();
// OData: /Events?$select=Name,EventID,Country
// Proxy Class
val query = DataQuery().select(Event.name, Event.eventID, Event.country)
val events = eventService.getEvents(query)
// Dynamic API
val query = DataQuery()
.from(eventSet)
.select(eventNameProp, eventEventIDProp, eventCountryProp)
val events = dataService.executeQuery(query).entityList
Exception will be thrown if non-selected properties are accessed.
loadEntity
¶
Use loadEntity
to retrieve an entity using its entity key or readLink
. If both are set, entity key takes precedence. If neither is set, an exception will be thrown.
Using Entity Key
// Proxy Class
Event event = new Event();
event.setEventID(1000L);
eventService.loadEntity(event);
// Dynamic API
EntityValue event = EntityValue.ofType(eventType);
eventEventIDProp.setLong(event, 1000L);
dataService.loadEntity(event);
// Proxy Class
val event = Event()
event.eventID = 1000L
eventService.loadEntity(event)
// Dynamic API
val event = EntityValue.ofType(eventType)
eventEventIDProp.setLong(event, 1000L)
dataService.loadEntity(event)
Event event = new Event();
event.setReadLink("...");
eventService.loadEntity(event);
// Dynamic API
EntityValue event = EntityValue.ofType(eventType);
event.setReadLink("...");
dataService.loadEntity(event);
// Proxy Class
val event = Event()
event.readLink = "..."
eventService.loadEntity(event)
// Dynamic API
val event = EntityValue.ofType(eventType)
event.readLink = "..."
dataService.loadEntity(event)
When using proxy class, calling the default constructor will create a new event with each property set to its default value. Therefore, the following code will likely result in an "entity not found" exception due to the EventID
being set to its default - zero.
Event event = new Event();
// Likely result in an entity not found exception because EventID is 0L
eventService.loadEntity(event);
// Create a new event without defaults for any property
event = new Event(false);
// An exception will be thrown because no identification is provided
eventService.loadEntity(event);
var event = Event()
// Likely result in an entity not found exception because EventID is 0L
eventService.loadEntity(event)
// Create a new event without defaults for any property
event = Event(false)
// An exception will be thrown because no identification is provided
eventService.loadEntity(event)
You can use LoadEntity
to lazy load properties, especially navigation properties for an entity that has already been retrieved. For example, when the list of features of an event is to be retrieved and the details of the event is to be shown, avoiding the use of the potentially expensive expand query option in the initial query.
// Proxy Class
// An event is retrieved via a query, and its navigation properties are not selected
Event event = ...;
// Specify that the navigation properties, Features and Theme, are to be loaded
DataQuery expandQuery = new DataQuery().expand(Event.features, Event.theme);
eventService.loadEntity(event, expandQuery);
// Dynamic API
// An event is retrieved via a query, and its navigation properties are not selected
EntityValue event = ...;
// Specify that the navigation properties, Features and Theme, are to be loaded
DataQuery expandQuery = new DataQuery().expand(eventFeaturesProp, eventThemeProp);
dataService.loadEntity(event, expandQuery);
// Proxy Class
// An event is retrieved via a query, and its navigation properties are not selected
val event = ...
// Specify that the navigation properties, Features and Theme, are to be loaded
val expandQuery = DataQuery().expand(Event.features, Event.theme)
eventService.loadEntity(event, expandQuery)
// Dynamic API
// An event is retrieved via a query, and its navigation properties are not selected
val event = ...
// Specify that the navigation properties, Features and Theme, are to be loaded
val expandQuery = DataQuery().expand(eventFeaturesProp, eventThemeProp)
dataService.loadEntity(event, expandQuery)
loadProperty
¶
While we can use loadEntity
to retrieve properties of an entity, complex retrieval conditions can only be retrieved using loadProperty
. In the example below, we are loading the top 50 sessions belonging to the event sorted by the name of the track associated with each session.
// Proxy Class
// An event is retrieved via a query
Event event = ...;
// Create a path to represent the name of the track associated with the session
DataPath path = Session.track.path(Track.name);
// Define the retrieval criteria for sessions associated with this particular event
DataQuery sortQuery = new DataQuery().expand(Session.track).top(50).orderBy(path);
eventService.loadProperty(Event.sessions, event, sortQuery);
// Dynamic API
// An event is retrieved via a query
EntityValue event = ...;
// Create a path to represent the name of the track associated with the session
DataPath path = sessionTrackProp.path(trackNameProp);
// Define the retrieval criteria for sessions associated with this particular event
DataQuery sortQuery = new DataQuery().expand(sessionTrackProp).top(50).orderBy(path);
dataService.loadProperty(eventSessionsProp, event, sortQuery);
// Proxy Class
// An event is retrieved via a query
val event = ...
// Create a path to represent the name of the track associated with the session
val path = Session.track.path(Track.name)
// Define the retrieval criteria for sessions associated with this particular event
val sortQuery = DataQuery().expand(Session.track).top(50).orderBy(path)
eventService.loadProperty(Event.sessions, event, sortQuery)
// Dynamic API
// An event is retrieved via a query
val event = ...
// Create a path to represent the name of the track associated with the session
val path = sessionTrackProp.path(trackNameProp)
// Define the retrieval criteria for sessions associated with this particular event
val sortQuery = DataQuery().expand(sessionTrackProp).top(50).orderBy(path)
dataService.loadProperty(eventSessionsProp, event, sortQuery)
If the EventID
foreign key is exposed in Session entity type, we can do the same with a DataQuery
against the Session collection.
// Proxy Class
DataPath path = Session.track.path(Track.name);
DataQuery query = new DataQuery().filter(Session.eventID.equal(1000L))
.expand(Session.track)
.top(50)
.orderBy(path);
List<Session> sessions = eventService.getSessions(query);
// Dynamic API
DataPath path = sessionTrackProp.path(trackNameProp);
DataQuery query = new DataQuery().filter(sessionEventIDProp.equal(1000L))
.expand(sessionTrackProp)
.top(50)
.orderBy(path);
EntityValueList sessions = dataService.executeQuery(query).getEntityList();
// Proxy Class
val path = Session.track.path(Track.name)
val query = DataQuery().filter(Session.eventID.equal(1000L))
.expand(Session.track)
.top(50)
.orderBy(path)
val sessions = eventService.getSessions(query)
// Dynamic API
val path = sessionTrackProp.path(trackNameProp)
val query = new DataQuery().filter(sessionEventIDProp.equal(1000L))
.expand(sessionTrackProp)
.top(50)
.orderBy(path)
val sessions = dataService.executeQuery(query).entityList
Media Download¶
OData provides two specific media metadata: media entity (think of a media entity as the metadata describing the binary data in the stream), and entity with stream properties. There are different strategies used to access online and offline media resources.
Offline¶
Offline only supports OData version 2 currently, and version 2 doesn’t support stream properties.
Suppose FILE_DOWNLOADSET
is an entityset
, when you use something like:
//OfflineDataDefiningQuery(String name, String query, boolean automaticallyRetrievesStreams)
new OfflineDataDefiningQuery("FILE_DOWNLOADSET", "FILE_DOWNLOADSET", true);
//OfflineDataDefiningQuery(String name, String query, boolean automaticallyRetrievesStreams)
OfflineDataDefiningQuery("FILE_DOWNLOADSET", "FILE_DOWNLOADSET", true)
Offline will first try to get all entities of the entity set by performing a GET /FILE_DOWNLOADSET
request.
And for each FILE_DOWNLOAD
inside FILE_DOWNLOADSET
, Offline will follow its media_src
link to download media, if any:
GET FILE_DOWNLOADSET(1)/$value
.
If you want to download an individual stream:
//OfflineDataDefiningQuery(String name, String query, boolean automaticallyRetrievesStreams)
new OfflineDataDefiningQuery("FILE_DOWNLOADSET", "FILE_DOWNLOADSET(1)", true);
//OfflineDataDefiningQuery(String name, String query, boolean automaticallyRetrievesStreams)
OfflineDataDefiningQuery("FILE_DOWNLOADSET", "FILE_DOWNLOADSET(1)", true)
Offline will still need to call GET FILE_DOWNLOADSET(1)
to get entity properties and metadata.
And then follow the media_src
link to download FILE_DOWNLOADSET(1)/$value
.
If, in that defining query, you set automaticallyRetrievesStreams
to be true, it will also pull down the media stream for each media entity. At that point, you can access the stream locally by using the media stream's read link. If your automaticallyRetrievesStreams
is set to false, it will still pull down the media entities (i.e. the metadata) but not the streams, so if you try to access the stream locally, it won't find anything. In that scenario, you would need to request()
that the stream for individual media entities be downloaded by adding defining requests on a per-media entity basis, and then downloading/refreshing, at which point the stream can be accessed locally.
However, we suggest you set automaticallyRetrievesStreams
to be true, as the back-end service may not implement GET
requests for individual entities.
After the media entity is pulled down along with its media stream, use:
DataService.downloadMediaAsync(entity, successHandler, failureHandler);
DataService.downloadMediaAsync(entity, successHandler, failureHandler)
to load the media stream from the media entity. successHandler
is called when the entity is successfully loaded and failureHandler
is called if a failure occurred during loading.
If the entity is successfully loaded, its media stream can be decoded using BitmapFactory
. Usually, this is implemented in successHandler
. Here is a simple example:
DataService.downloadMediaAsync(mediaEntity, media -> {
Drawable image = new BitmapDrawable(Resources, BitmapFactory.decodeByteArray(media, 0, media.length));
}, error -> {
LOGGER.debug("Error encountered during load of media resource", error);
DataService.downloadMediaAsync(mediaEntity, { media ->
val image = BitmapDrawable(Resources, BitmapFactory.decodeByteArray(media, 0, media.size))
}, { error ->
LOGGER.debug("Error encountered during load of media resource", error)
In the example above, the media stream is decoded by BitmapFactory
and then passed to BitmapDrawable
to generate an image. Otherwise, an error message will be generated.
Additional information can be found: Offline OData Media Resources
Online¶
In this scenario, you don't have to download entity sets in advance. Data is downloaded by the application using the OData service base URL. So, the only concern is how to access the media resource.
Because the way to access media data of a media entity and an entity with stream properties is different, we first have to check each entity as to whether it is a media entity or an entity with stream properties. Suppose EntityValue
is the entity that you want to handle, use
EntityValue.getEntityType().isMedia();
EntityValue.entityType.isMedia
to check if it is a media entity. Use the code below to check whether it has stream properties:
EntityValue.getEntityType().getStreamProperties().length() > 0
EntityValue.entityType.streamProperties.length() > 0
Unlike the Offline scenario, we need the media resource URL as well as the OData base URL to pull down the media stream for each entity.
Media entity
For media entities, use the following to get the media resource URL.
String mediaLinkURL = EntityValue.getMediaStream().getReadLink();