blob: 78e5230560838c9f7a03383ef34af7e9c50446d3 [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Apache Olingo provides libraries which enable developers to implement OData producers and OData consumers. The available OData Java library implements OData version 2.0. In future on goal is to provide an OData 4.0 compliant library once the OData standard is published at OASIS. The focus within the community is currently on the Java technology but it is up to the community to discuss if other environments find interest.">
<meta name="author" content="">
<link rel="icon" href="/favicon.ico">
<title>Apache Olingo Library</title>
<!-- Bootstrap core CSS -->
<link href="/css/bootstrap.css" rel="stylesheet" type="text/css"><!-- Custom styles for this template -->
<link href="/css/navbar.css" rel="stylesheet" type="text/css"><!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
<link href="/css/offcanvas.css" rel="stylesheet" type="text/css"><!-- Custom styles for this template -->
<link rel="stylesheet" href="/css/main.css">
<!--[if lt IE 9]><script src="/js/ie8-responsive-file-warning.js"></script><![endif]-->
<style>
.headerlink {
visibility: hidden;
}
dt:hover > .headerlink, p:hover > .headerlink, td:hover > .headerlink, h1:hover > .headerlink, h2:hover > .headerlink, h3:hover > .headerlink, h4:hover > .headerlink, h5:hover > .headerlink, h6:hover > .headerlink {
visibility: visible
} </style>
<script src="/js/ie-emulation-modes-warning.js" type="text/javascript">
</script><!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="/js/ie10-viewport-bug-workaround.js" type="text/javascript">
</script><!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="/js/html5shiv.min.js"></script>
<script src="/js/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
<!-- Static navbar -->
<div class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<img class="navbar-brand" src="/img/OlingoOrangeTM.png" style="width:62px;" >
<a class="navbar-brand" href="/">Apache Olingo™</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">ASF <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="http://www.apache.org/foundation/">ASF Home</a></li>
<li><a href="http://projects.apache.org/">Projects</a></li>
<li><a href="http://people.apache.org/">People</a></li>
<li><a href="http://www.apache.org/foundation/getinvolved.html">Get Involved</a></li>
<li><a href="http://www.apache.org/dyn/closer.cgi">Download</a></li>
<li><a href="http://www.apache.org/security/">Security</a></li>
<li><a href="http://www.apache.org/foundation/sponsorship.html">Support Apache</a></li>
</ul>
</li>
<li><a href="http://www.apache.org/licenses/">License</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Download <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="/doc/odata2/download.html">Download OData 2.0 Java</a></li>
<li><a href="/doc/odata4/download.html">Download OData 4.0 Java</a></li>
<li><a href="/doc/javascript/download.html">Download OData 4.0 JavaScript</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Documentation <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="/doc/odata2/index.html">Documentation OData 2.0 Java</a></li>
<li><a href="/doc/odata4/index.html">Documentation OData 4.0 Java</a></li>
<li><a href="/doc/javascript/index.html">Documentation OData 4.0 JavaScript</a></li>
</ul>
</li>
<li><a href="/support.html">Support</a></li>
<li><a href="/contribute.html">Contribute</a></li>
</ul>
<a class="navbar-right" href="http://www.apache.org/foundation/" target="_blank">
<img class="navbar-right" height="50px" src="/img/asf_logo_url.svg" alt="Apache Software Foundation">
</a>
</div><!--/.nav-collapse -->
</div><!--/.container-fluid -->
</div><!-- Main component for a primary marketing message or call to action -->
<h1 id="how-to-build-an-odata-service-with-olingo-v4">How to build an OData Service with Olingo V4<a class="headerlink" href="#how-to-build-an-odata-service-with-olingo-v4" title="Permalink">&para;</a></h1>
<h1 id="part-9-handling-deep-insert-requests">Part 9: Handling "Deep Insert" requests<a class="headerlink" href="#part-9-handling-deep-insert-requests" title="Permalink">&para;</a></h1>
<h2 id="introduction">Introduction<a class="headerlink" href="#introduction" title="Permalink">&para;</a></h2>
<p>In the present tutorial, we will implement the handling of deep insert requests.</p>
<p><strong>Note:</strong>
The final source code can be found in the project <a href="https://gitbox.apache.org/repos/asf/olingo-odata4">git repository</a>.
A detailed description how to checkout the tutorials can be found <a href="/doc/odata4/tutorials/prerequisites/prerequisites.html">here</a>.<br/>
This tutorial can be found in subdirectory /samples/tutorials/p12_deep_insert</p>
<p><strong>Table of Contents</strong></p>
<ol>
<li>Introduction</li>
<li>Preparation</li>
<li>Implementation</li>
<li>Run the implemented service</li>
<li>Links</li>
</ol>
<h1 id="1-introduction">1. Introduction<a class="headerlink" href="#1-introduction" title="Permalink">&para;</a></h1>
<p>In this tutorial shows how to handle "deep insert" requests. OData gives us the possibility to create related entities, and bind existing entities to a new created entity in a single request. (More detailed information: <a href="http://docs.oasis-open.org/odata/odata/v4.0/errata02/os/complete/part1-protocol/odata-v4.0-errata02-os-part1-protocol-complete.html#_Toc406398326">OData Version 4.0 Part 1: Protocol</a>, <a href="http://docs.oasis-open.org/odata/odata-json-format/v4.0/errata02/os/odata-json-format-v4.0-errata02-os-complete.html#_Toc403940637">OData JSON Format Version 4.0</a>)</p>
<p>OData uses to create a related entity the same syntax as for an expanded navigation property, as descripted in <a href="http://docs.oasis-open.org/odata/odata-json-format/v4.0/errata02/os/odata-json-format-v4.0-errata02-os-complete.html#_Toc403940637">OData JSON Format</a>. To bind an existing entity, OData uses the <code>odata.bind</code> property annotation. The value of the annotation is either an entity-Id or a collection of entity-Ids. An <a href="http://docs.oasis-open.org/odata/odata/v4.0/errata02/os/complete/part1-protocol/odata-v4.0-errata02-os-part1-protocol-complete.html#_Toc406398204">entity-Id</a> is a durable, opaque, globally unique <a href="https://www.ietf.org/rfc/rfc3987.txt">IRI</a>. The specification recommends to use the canonical URL of the entity. In this tutorial the relative canonical URL of an entity is used.</p>
<p><strong>Example</strong></p>
<p>For example you may want to create a new category and also create new products, which are related to the new created category. In addition you would like to bind an existing product to the new created category.
Such a request is issued againest the URL of the entity set.</p>
<p>In this example, a new Category "Food" and two products ("Bread", "Milk") are created. In addition the Product with the key 5 is bind to the just created entity.</p>
<pre><code>POST /Categories HTTP/1.1
Content-Type: application/json
{
"Name": "Food",
"Products@odata.bind": [
"Products(5)"
],
"Products": [
{
"Name": "Bread",
"Description": "Whole grain bread"
},
{
"Name": "Milk",
"Description": "Low fat milk"
}
]
}
</code></pre>
<h1 id="2-preparation">2. Preparation<a class="headerlink" href="#2-preparation" title="Permalink">&para;</a></h1>
<p>You should read the previous tutorials first to have an idea how to read and write entities. In addition the following code is based on the write tutorial merged with the navigation tutorial. <strong>It is strongly recommended to have a look at the prepared code.</strong> There are some changes how related entites are linked together. More details are descripted in the implementation chapter.</p>
<p>As a shortcut you should checkout the prepared tutorial project in the <a href="https://gitbox.apache.org/repos/asf/olingo-odata4">git repository</a> in folder /samples/tutorials/p12_deep_insert_preparation.</p>
<p>Afterwards do a Deploy and run: it should be working. At this state you can perform CRUD operations and do navigations between products and categories.</p>
<h1 id="3-implementation">3. Implementation<a class="headerlink" href="#3-implementation" title="Permalink">&para;</a></h1>
<p>Before we start with the implementation, please have a look at the class <code>myservice.mynamespace.data.Storage</code>. In difference to the <a href="/doc/odata4/tutorials/navigation/tutorial_navigation.html">navigation tutorial</a> the relations between two entities can not be hard coded because we would like to create and change relations between entities dynamically. In the constructor of the data storage the creation of the sample data is called. After that the method <code>linkProductsAndCategories</code>is called. This methods sets a few links between the just created entities. <strong>The linked entites are stored as navigation links</strong></p>
<p>To express the relation between two entities, Olingo uses the class <a href="/javadoc/odata4/org/apache/olingo/commons/api/data/Link.html">Link</a>. This class is used for related entites (directly connected via Java references) and bindings (which are actually strings) to other entities. To get the related entites for a particual navigation property, you can ask an entity with the method <a href="/javadoc/odata4/org/apache/olingo/commons/api/data/Linked.html#getNavigationLink(java.lang.String)"><code>getNavigationLink(String name)</code></a> for an navigation property link. The link will contain either an entity or a collection of entities dependenting on the type of the navigation property. To get the actual entities use the methods <a href="/javadoc/odata4/org/apache/olingo/commons/api/data/Link.html#getInlineEntity()"><code>getInlineEntity()</code></a> or <a href="http://javadoc/odata4/org/apache/olingo/commons/api/data/Link.html#getInlineEntitySet()"><code>getInlineEntitySet()</code></a>
The same can be done for bindings via the method <a href="/javadoc/odata4/org/apache/olingo/commons/api/data/Linked.html#getNavigationBinding(java.lang.String)"><code>getNavigationBinding(String name)</code></a>. The values of the Binding can be gotten by the methods <a href="/javadoc/odata4/org/apache/olingo/commons/api/data/Link.html#getBindingLink()"><code>getBindingLink()</code></a> and <a href="/javadoc/odata4/org/apache/olingo/commons/api/data/Link.html#getBindingLinks()"><code>getBindingLinks()</code></a>.</p>
<p>The point is that the Entity deserializer uses the same concept to represent the payload as Java objects.
Please have a look at the figure below. The deserializer returns an entity with two aggregated Link Objects which belons to the same navigation property "Product". The upper Link object stores the related entites, which have to be created. The lower Link contains the entity-ids to the already existing entities.
<img alt="Deserializer Result" src="before.png"/></p>
<p>When our implementation has processed the whole request, all entites are created and linked as <strong>navigationLinks</strong>.</p>
<p><img alt="After Deep insert" src="after.png"/></p>
<p>If one of the requests fail, or one of the binding links is invalid, none of the entities must be created.
The prepared implementation provides the methods <code>beginTransaction</code>, <code>rollbackTransaction</code> and <code>commitTransaction</code> in the data store to simulate a transactional behavior.
Those methods are called in the <code>DemoEntityProcessor</code> implementation. So if you throw an exception in the createEntity Method the transaction will automatically rolled back.</p>
<p>So let us begin with the implementation. In the previous tutorials the entity object returned by the deserializer is passed to the data store. Please open the class <code>myservice.mynamespace.data.Storage</code> and jump the method <code>createEntity</code>.</p>
<p>The implementation should look like the following:</p>
<pre><code class="language-java"> private Entity createEntity(EdmEntitySet edmEntitySet, EdmEntityType edmEntityType, Entity entity,
List&lt;Entity&gt; entityList, final String rawServiceUri) throws ODataApplicationException {
// 1.) Create the entity
final Entity newEntity = new Entity();
newEntity.setType(entity.getType());
// Create the new key for the entity
int newId = 1;
while (entityIdExists(newId, entityList)) {
newId++;
}
// Add all provided properties
newEntity.getProperties().addAll(entity.getProperties());
// Add the key property
newEntity.getProperties().add(new Property(null, "ID", ValueType.PRIMITIVE, newId));
newEntity.setId(createId(newEntity, "ID"));
// --&gt; Implement Deep Insert handling here &lt;--
entityList.add(newEntity);
return newEntity;
}
</code></pre>
<p>The implementation is split in two steps:</p>
<ul>
<li>Handle entity bindings</li>
<li>Handle related entities</li>
</ul>
<h2 id="handle-entity-bindings">Handle entity bindings<a class="headerlink" href="#handle-entity-bindings" title="Permalink">&para;</a></h2>
<p>To handle entity bindings we need two helper methods.</p>
<p>The first method takes an entity-Id and returns the addressed entity. The OData objects provides a helper object to parse entity-ids. (See <a href="/javadoc/odata4/org/apache/olingo/server/api/uri/UriHelper.html">UriHelper</a>). After parsing the id we have to check if the addressed entity set fits to the entity set the navigation property points to. After that the data is read by calling <code>readEntityData</code>.</p>
<pre><code class="language-java"> private Entity readEntityByBindingLink(final String entityId, final EdmEntitySet edmEntitySet,
final String rawServiceUri) throws ODataApplicationException {
UriResourceEntitySet entitySetResource = null;
try {
entitySetResource = odata.createUriHelper().parseEntityId(edm, entityId, rawServiceUri);
if(!entitySetResource.getEntitySet().getName().equals(edmEntitySet.getName())) {
throw new ODataApplicationException("Execpted an entity-id for entity set " + edmEntitySet.getName() +
" but found id for entity set " + entitySetResource.getEntitySet().getName(),
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH);
}
} catch (DeserializerException e) {
throw new ODataApplicationException(entityId + " is not a valid entity-Id",
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH);
}
return readEntityData(entitySetResource.getEntitySet(), entitySetResource.getKeyPredicates());
}
</code></pre>
<p>The second method helps us to link entities together. If the navigation property has partner navigation property the link is set in both directions.</p>
<pre><code class="language-java"> private void createLink(final EdmNavigationProperty navigationProperty, final Entity srcEntity,
final Entity destEntity) {
setLink(navigationProperty, srcEntity, destEntity);
final EdmNavigationProperty partnerNavigationProperty = navigationProperty.getPartner();
if (partnerNavigationProperty != null) {
setLink(partnerNavigationProperty, destEntity, srcEntity);
}
}
</code></pre>
<p>If the client has used the <code>odata.bind</code> property annotation, we can get the bindings by calling <code>getNavigationBindings()</code>. The implementation loops over all bindings and links the addressed entity to the new created one.</p>
<pre><code class="language-java"> // 2.1.) Apply binding links
for(final Link link : entity.getNavigationBindings()) {
final EdmNavigationProperty edmNavigationProperty = edmEntityType.getNavigationProperty(link.getTitle());
final EdmEntitySet targetEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
if(edmNavigationProperty.isCollection() &amp;&amp; link.getBindingLinks() != null) {
for(final String bindingLink : link.getBindingLinks()) {
final Entity relatedEntity = readEntityByBindingLink(bindingLink, targetEntitySet, rawServiceUri);
createLink(edmNavigationProperty, newEntity, relatedEntity);
}
} else if(!edmNavigationProperty.isCollection() &amp;&amp; link.getBindingLink() != null) {
final Entity relatedEntity = readEntityByBindingLink(link.getBindingLink(), targetEntitySet, rawServiceUri);
createLink(edmNavigationProperty, newEntity, relatedEntity);
}
}
</code></pre>
<h2 id="handle-related-entities">Handle related entities<a class="headerlink" href="#handle-related-entities" title="Permalink">&para;</a></h2>
<p>The creation of related entities is similar. First the implementation loops over all navigation properties with related entites in the payload. The simplest way to create releated entities is to call the method <code>createEntityData</code>. So the the implementation is called recursively and can handle deep inserts with arbitrary depth.</p>
<pre><code class="language-java"> // 2.2.) Create nested entities
for(final Link link : entity.getNavigationLinks()) {
final EdmNavigationProperty edmNavigationProperty = edmEntityType.getNavigationProperty(link.getTitle());
final EdmEntitySet targetEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
if(edmNavigationProperty.isCollection() &amp;&amp; link.getInlineEntitySet() != null) {
for(final Entity nestedEntity : link.getInlineEntitySet().getEntities()) {
final Entity newNestedEntity = createEntityData(targetEntitySet, nestedEntity, rawServiceUri);
createLink(edmNavigationProperty, newEntity, newNestedEntity);
}
} else if(!edmNavigationProperty.isCollection() &amp;&amp; link.getInlineEntity() != null){
final Entity newNestedEntity = createEntityData(targetEntitySet, link.getInlineEntity(), rawServiceUri);
createLink(edmNavigationProperty, newEntity, newNestedEntity);
}
}
</code></pre>
<h1 id="4-run-the-implemented-service">4. Run the implemented service<a class="headerlink" href="#4-run-the-implemented-service" title="Permalink">&para;</a></h1>
<p>After building and deploying your service to your server, you can try the following requests:</p>
<p>URI: <a href="http://localhost:8080/DemoService-DeepInsert/DemoService.svc/Categories"><code>http://localhost:8080/DemoService-DeepInsert/DemoService.svc/Categories</code></a><br/>
HTTP-Verb: POST<br/>
Content-Type: application/json<br/>
Payload:</p>
<pre><code>{
"name": "Food",
"Products@odata.bind": [
"Products(5)"
],
"Products": [
{
"Name": "Bread",
"Description": "Whole grain bread"
},
{
"Name": "Milk",
"Description": "Low fat milk"
}
]
}
</code></pre>
<p>After sending this requests, let us see if the enties have been created and linked togetger.
Send an GET request to <code>http://localhost:8080/DemoService-DeepInsert/DemoService.svc/Categories</code>.
If it is the first category you created, the new entity should have the key <code>3</code></p>
<p><img alt="Entity Set Categories" src="categories_entity_set.png"/></p>
<p>So send a request to fetch all related products for the "Food" category.
<a href=""><code>http://localhost:8080/DemoService-DeepInsert/DemoService.svc/Categories(3)/Products</code></a></p>
<p>As you can see the two products are created and linked to the category.</p>
<p><img alt='Related products for category "Food"' src="related_products.png"/></p>
<h1 id="5-links">5. Links<a class="headerlink" href="#5-links" title="Permalink">&para;</a></h1>
<h3 id="tutorials">Tutorials<a class="headerlink" href="#tutorials" title="Permalink">&para;</a></h3>
<p>Further topics to be covered by follow-up tutorials:</p>
<ul>
<li>Tutorial OData V4 service part 1: <a href="/doc/odata4/tutorials/read/tutorial_read.html">Read Entity Collection</a></li>
<li>Tutorial OData V4 service part 2: <a href="/doc/odata4/tutorials/readep/tutorial_readep.html">Read Entity, Read Property</a></li>
<li>Tutorial OData V4 service part 3: <a href="/doc/odata4/tutorials/write/tutorial_write.html">Write (Create, Update, Delete Entity)</a></li>
<li>Tutorial OData V4 service, part 4: <a href="/doc/odata4/tutorials/navigation/tutorial_navigation.html">Navigation</a></li>
<li>Tutorial OData V4 service, part 5.1: <a href="/doc/odata4/tutorials/sqo_tcs/tutorial_sqo_tcs.html">System Query Options $top, $skip, $count (this page)</a></li>
<li>Tutorial OData V4 service, part 5.2: <a href="/doc/odata4/tutorials/sqo_es/tutorial_sqo_es.html">System Query Options $select, $expand</a></li>
<li>Tutorial OData V4 service, part 5.3: <a href="/doc/odata4/tutorials/sqo_o/tutorial_sqo_o.html">System Query Options $orderby</a></li>
<li>Tutorial OData V4 service, part 5.4: <a href="/doc/odata4/tutorials/sqo_f/tutorial_sqo_f.html">System Query Options $filter</a></li>
<li>Tutorial OData V4 service, part 6: <a href="/doc/odata4/tutorials/action/tutorial_action.html">Action and Function Imports</a></li>
<li>Tutorial OData V4 service, part 7: <a href="/doc/odata4/tutorials/media/tutorial_media.html">Add Media entities to the service</a></li>
<li>Tutorial OData V4 service, part 8: <a href="/doc/odata4/tutorials/batch/tutorial_batch.html">Batch Request support</a></li>
<li>Tutorial OData V4 service, part 9: Handling "Deep Insert" requests</li>
</ul>
<h3 id="code-and-repository">Code and Repository<a class="headerlink" href="#code-and-repository" title="Permalink">&para;</a></h3>
<ul>
<li><a href="https://gitbox.apache.org/repos/asf/olingo-odata4">Git Repository</a></li>
<li><a href="/doc/odata4/tutorials/prerequisites/prerequisites.html">Guide - To fetch the tutorial sources</a></li>
<li><a href="http://www.apache.org/dyn/closer.lua/olingo/odata4/4.0.0/DemoService_Tutorial.zip">Demo Service source code as zip file (contains all tutorials)</a></li>
</ul>
<h3 id="further-reading">Further reading<a class="headerlink" href="#further-reading" title="Permalink">&para;</a></h3>
<ul>
<li><a href="http://odata.org/">Official OData Homepage</a></li>
<li><a href="http://www.odata.org/documentation/">OData documentation</a></li>
<li><a href="/javadoc/odata4/index.html">Olingo Javadoc</a></li>
</ul>
<div align="center">
<p>Copyright © 2013-2022, The Apache Software Foundation<br>
Apache Olingo, Olingo, Apache, the Apache feather, and
the Apache Olingo project logo are trademarks of the Apache Software
Foundation.</p>
<small><a href="/doc/odata2/privacy.html">Privacy</a></small>
</div>
</div><!-- /container -->
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="/js/jquery.js" type="text/javascript">
</script>
<script src="/js/bootstrap.js" type="text/javascript">
</script>
<script src="/js/offcanvas.js" type="text/javascript">
</script>
<link rel="stylesheet" href="/css/docco.css">
<script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.0.1/build/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
</body>
</html>