blob: fa1fe4ea468c16ecd47f03d416f70cdc81f2ed76 [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="adding-function-imports-to-odata-services-with-the-jpa-processor">Adding Function Imports to OData Services with the JPA Processor<a class="headerlink" href="#adding-function-imports-to-odata-services-with-the-jpa-processor" title="Permalink">&para;</a></h1>
<hr/>
<p>Function imports are used to perform custom operations on a JPA entity in addition to CRUD operations. For example, consider a scenario where you would like to check the availability of an item to promise on the sales order line items. ATP check is a custom operation that can be exposed as a function import in the schema of OData service.</p>
<p>OData JPA Processor Library is enhanced to:</p>
<ul>
<li>
<p>Enable Custom Operations as Function Imports</p>
</li>
<li>
<p>Add non JPA entity types as Complex Types to the EDM and use the same as Function Imports Return Type</p>
</li>
</ul>
<h3 id="enabling-custom-operations-as-function-imports">Enabling Custom Operations as Function Imports<a class="headerlink" href="#enabling-custom-operations-as-function-imports" title="Permalink">&para;</a></h3>
<ol>
<li>
<p>Create a dependency to EDM Annotation Project. This is required to use the annotations that are defined in the project.</p>
<dependency>
<groupid>org.apache.olingo</groupid>
<artifactid>olingo-odata2-api-annotation</artifactid>
<version>x.x.x</version>
<scope>provided</scope>
</dependency>
</li>
<li>
<p>Create a Java class and annotate the Java methods implementing custom operations with Function Import and Parameter Java annotations as shown below. Java methods can be created in JPA entity types and these methods can be annotated with EDM annotations for function import.</p>
<pre><code> package org.apache.olingo.odata2.jpa.processor.ref.extension;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Persistence;
import javax.persistence.Query;
import org.apache.olingo.odata2.api.annotation.edm.EdmFacets;
import org.apache.olingo.odata2.api.annotation.edm.EdmFunctionImport;
import org.apache.olingo.odata2.api.annotation.edm.EdmFunctionImport.HttpMethod;
import org.apache.olingo.odata2.api.annotation.edm.EdmFunctionImport.ReturnType;
import org.apache.olingo.odata2.api.annotation.edm.EdmFunctionImport.ReturnType.Type;
import org.apache.olingo.odata2.api.annotation.edm.EdmFunctionImportParameter;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.jpa.processor.ref.model.Address;
import org.apache.olingo.odata2.jpa.processor.ref.model.SalesOrderHeader;
import org.apache.olingo.odata2.jpa.processor.ref.model.SalesOrderItem;
public class SalesOrderHeaderProcessor {
private EntityManager em;
public SalesOrderHeaderProcessor() {
em = Persistence.createEntityManagerFactory("salesorderprocessing")
.createEntityManager();
}
@SuppressWarnings("unchecked")
@EdmFunctionImport(name = "FindAllSalesOrders", entitySet = "SalesOrders", returnType = @ReturnType(
type = Type.ENTITY, isCollection = true))
public List&lt;SalesOrderHeader&gt; findAllSalesOrders(
@EdmFunctionImportParameter(name = "DeliveryStatusCode",
facets = @EdmFacets(maxLength = 2)) final String status) {
Query q = em
.createQuery("SELECT E1 from SalesOrderHeader E1 WHERE E1.deliveryStatus = '"
+ status + "'");
List&lt;SalesOrderHeader&gt; soList = (List&lt;SalesOrderHeader&gt;) q
.getResultList();
return soList;
}
@EdmFunctionImport(name = "CheckATP", returnType = @ReturnType(type = Type.SIMPLE, isCollection = false),
httpMethod = HttpMethod.GET)
public boolean checkATP(
@EdmFunctionImportParameter(name = "SoID", facets = @EdmFacets(nullable = false)) final Long soID,
@EdmFunctionImportParameter(name = "LiId", facets = @EdmFacets(nullable = false)) final Long lineItemID) {
if (soID == 2L) {
return false;
} else {
return true;
}
}
@EdmFunctionImport(returnType = @ReturnType(type = Type.ENTITY, isCollection = true), entitySet = "SalesOrders")
public SalesOrderHeader calculateNetAmount(
@EdmFunctionImportParameter(name = "SoID", facets = @EdmFacets(nullable = false)) final Long soID)
throws ODataException {
if (soID &lt;= 0L) {
throw new ODataException("Invalid SoID");
}
Query q = em
.createQuery("SELECT E1 from SalesOrderHeader E1 WHERE E1.soId = "
+ soID + "l");
if (q.getResultList().isEmpty()) {
return null;
}
SalesOrderHeader so = (SalesOrderHeader) q.getResultList().get(0);
double amount = 0;
for (SalesOrderItem soi : so.getSalesOrderItem()) {
amount = amount
+ (soi.getAmount() * soi.getDiscount() * soi.getQuantity());
}
so.setNetAmount(amount);
return so;
}
@SuppressWarnings("unchecked")
@EdmFunctionImport(returnType = @ReturnType(type = Type.COMPLEX))
public Address getAddress(
@EdmFunctionImportParameter(name = "SoID", facets = @EdmFacets(nullable = false)) final Long soID) {
Query q = em
.createQuery("SELECT E1 from SalesOrderHeader E1 WHERE E1.soId = "
+ soID + "l");
List&lt;SalesOrderHeader&gt; soList = (List&lt;SalesOrderHeader&gt;) q
.getResultList();
if (!soList.isEmpty()) {
return soList.get(0).getCustomer().getAddress();
} else {
return null;
}
}
@EdmFunctionImport(returnType = @ReturnType(type = Type.COMPLEX))
public OrderValue orderValue(
@EdmFunctionImportParameter(name = "SoId", facets = @EdmFacets(nullable = false)) final Long soID) {
Query q = em
.createQuery("SELECT E1 from SalesOrderHeader E1 WHERE E1.soId = "
+ soID + "l");
if (q.getResultList().isEmpty()) {
return null;
}
SalesOrderHeader so = (SalesOrderHeader) q.getResultList().get(0);
double amount = 0;
for (SalesOrderItem soi : so.getSalesOrderItem()) {
amount = amount
+ (soi.getAmount() * soi.getDiscount() * soi.getQuantity());
}
OrderValue orderValue = new OrderValue();
orderValue.setAmount(amount);
orderValue.setCurrency(so.getCurrencyCode());
return orderValue;
}
}
</code></pre>
</li>
<li>
<p>Create a Java class by implementing the interface <em>org.apache.olingo.odata2.jpa.processor.api.model</em> to register the annotated Java methods.</p>
<pre><code class="language-java"> public class SalesOrderProcessingExtension implements JPAEdmExtension {
@Override
public void extendJPAEdmSchema(final JPAEdmSchemaView arg0 {
// TODO Auto-generated method stub
}
@Override
public void extendWithOperation(final JPAEdmSchemaView view) {
view.registerOperations(SalesOrderHeaderProcessor.class, null);
}
}
</code></pre>
</li>
</ol>
<p><em>Note</em>: Use the method <em>extendWithOperation</em> to register the list of classes and the methods within the class that needs to be exposed as Function Imports. If the second parameter is passed null, then the OData JPA Processor Library would consider all the annotated methods within the class for Function Import. However, you could also restrict the list of methods that needs to be transformed into function imports within a Java class by passing an array of Java method names as the second parameter.</p>
<ol start="4">
<li>
<p>Register the class created in step 3 with <em>ODataJPAContext</em> as shown below. The registration can be done during the initialization of <em>ODataJPAContext</em> in OData JPA Service Factory along with initializing persistence unit name, entity manager factory instance and optional mapping model.</p>
<pre><code class="language-java"> oDataJPAContext.setJPAEdmExtension((JPAEdmExtension) new SalesOrderProcessingExtension());
</code></pre>
</li>
</ol>
<p><em>Note</em>: You must register the class because the OData JPA Processor Library should be informed about the list of Java methods that it needs to process in a project. If we do not register, then OData JPA Processor Library should scan all the classes and the methods in the Java project looking for EDM annotations. In order to avoid such overload, it is mandatory to specify the list of Java methods that shall be transformed into function imports in a class.</p>
<h3 id="using-non-jpa-entities-added-to-the-edm-as-function-imports-return-type">Using Non JPA Entities added to the EDM as Function Imports Return Type<a class="headerlink" href="#using-non-jpa-entities-added-to-the-edm-as-function-imports-return-type" title="Permalink">&para;</a></h3>
<p><strong>Prerequisite</strong></p>
<p>Add non JPA Entity Types as Complex Types to the EDM. See <a href="/doc/odata2/tutorials/ExtendingtheEDM.html">Extending the EDM Generated from the JPA Models</a> for more information.</p>
<p><em>Note</em>: The Simple Name of the Java class used as the return type in a Function Import and the name of the EDM Complex Type should be same.</p>
<p>Here is an example, you define the operations inside the <code>SalesOrderHeaderProcessor</code> class and then register this class inside <code>JPAEdmExtension</code> class <code>extendWithOperation</code>.</p>
<h5 id="sample-code">Sample Code<a class="headerlink" href="#sample-code" title="Permalink">&para;</a></h5>
<pre><code> @EdmFunctionImport(returnType = @ReturnType(type = Type.COMPLEX))
public OrderValue orderValue(
@EdmFunctionImportParameter(name = "SoId", facets = @EdmFacets(nullable = false)) final Long soID) {
Query q = em
.createQuery("SELECT E1 from SalesOrderHeader E1 WHERE E1.soId = "
+ soID + "l");
if (q.getResultList().isEmpty()) {
return null;
}
SalesOrderHeader so = (SalesOrderHeader) q.getResultList().get(0);
double amount = 0;
for (SalesOrderItem soi : so.getSalesOrderItem()) {
amount = amount
+ (soi.getAmount() * soi.getDiscount() * soi.getQuantity());
}
OrderValue orderValue = new OrderValue();
orderValue.setAmount(amount);
orderValue.setCurrency(so.getCurrencyCode());
return orderValue;
}
</code></pre>
<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>