Jump to: navigation, search

Difference between revisions of "EclipseLink/Development/2.4.0/JPA-RS/REST-API"

(Multi-Tenancy)
 
(76 intermediate revisions by 3 users not shown)
Line 1: Line 1:
__NOTOC__
+
[http://www.eclipse.org/eclipselink/documentation/2.4/solutions/restful_jpa003.htm#CHDEGJIG JPA-RS API Documentation version 2.4.0]
= JAX-RS: A RESTful API for JPA=
+
 
+
'''History'''
+
Date
+
Author
+
Description
+
01/10/11
+
dclarke
+
Initial Version
+
 
+
{|{{BMTableStyle}}
+
|-{{BMTHStyle}}
+
! Date
+
! Author
+
! Description
+
|-
+
| Oct 1, 2011 || dclarke || Initial Version
+
|-
+
| Nov 9, 2011 || dclarke || Moved to wiki for community review and feedback.
+
|-
+
| Jan-Mar, 2012 || tware || updates and additions to REST API descriptions
+
|}
+
 
+
==Overview==
+
 
+
This specification will define a RESTful API for dealing with JPA. The intent is to simplify how JPA persistence units can be accessed using REST with JSON or XML formatted messages. A JPA-RS runtime will provide access to all persistence units packaged in the same application that it is running in as well as any dynamic persistence unit that is provisioned within it.
+
 
+
===URI Root ===
+
The root URI for the RESTful interface is defined in the application's web.xml:
+
  <servlet-mapping>
+
    <servlet-name>Jersey REST Service</servlet-name>
+
    <url-pattern>/persistence/*</url-pattern>
+
  </servlet-mapping>
+
 
+
Here the root URI is 'persistence' to differentiate it from other services.
+
 
+
''Note: "persistence" will be used as the root for all examples in this document''
+
 
+
== Persistence Unit Operations==
+
 
+
The JPA-RS URI structure then requires a persistence unit name: '''/persistence/{unit-name}'''. Assuming this is a valid persistence unit in the give JPA-RS application context the following high level operations are available.
+
 
+
* ENTITY: /persistence/{unit-name}/entity
+
* QUERY: /persistence/{unit-name}/query
+
* METADATA: /persistence/{unit-name}/metadata
+
 
+
=== HTTP Method Basics ===
+
 
+
The HTTP methods used in JPA-RS with there basic interpretation in persistence are:
+
 
+
* GET:
+
* PUT: enclosed entity be stored under the supplied Request-URI
+
** INSERT when the PK entity does not exist
+
** UPDATE when the entity does exist
+
* POST: new subordinate of resource identified by the request-URI
+
* DELETE:
+
 
+
The HEAD, TRACE, and CONNECT methods currently have no defined meaning in JPA-RS
+
 
+
===Data Formats: JSON or XML===
+
 
+
This REST interface deals with XML and JSON representations of the data equally well. The caller is responsible for using the HTTP header values to indicate the format of the content it is sending (Content-Type = application/json or application/xml) as well as indicating the format of the result it expects (Accept = application/json or application/xml).
+
In cases where no header value is specified JSON will be used by default and in cases where content-type is specified and accept is not the returned format will match the content-type passed in.
+
NOTE: In many REST utilities the accept value is defaulted to application/xml making it the users responsibility to configure this value explicitly.
+
 
+
 
+
===Web Caching===
+
In addition to the internal caching within TopLink (EclipseLink) the results from REST URI calls can be cached in various points between the initiating server and the user, including their browser. This caching is determined by URI structure and the HTTP header information in the response from the REST calls. Generally only GET call responses are cached so these must be addressed carefully to ensure the proper caching information is provided so that the end user of the RESTful persistence interface get the most correct information possible while still benefiting from web caching.
+
TODO: Add guidelines
+
 
+
===HTTP Header Fields ===
+
 
+
'''Standard HTTP'''
+
* If-Match - conditional
+
* Warning
+
 
+
'''EclipseLink JPA-RS'''
+
* Refresh
+
 
+
=== Multi-Tenancy ===
+
 
+
When using EclipseLink's multi-tenancy features developers can choose to isolate their tenant's data by schema/table (@Multitenant(TABLE_PER_TENANT) - EclipseLink 2.4) or within a shared table (@Multitenant(SINGLE_TABLE) - EclipseLink 2.3). In order to access a persistence unit that supports concurrent access by multiple tenants the persistence context must specify tenant discriminator values within its properties. To support this usage within JPA-RS the tenant identifier values will be supplied using matrix parameters within the persistence unit portion of the URI.
+
 
+
<pre>
+
/persistence/{unit-name};mysports.league=HTHL/
+
</pre>
+
 
+
== Entity Operations: /persistence/{unit-name}/entity/* ==
+
 
+
Entity operations are those performed against a specific entity type within the persistence unit. The {type} value refers to the type name (descriptor alias).
+
 
+
=== FIND ===
+
 
+
* GET /persistence/{unit-name}/entity/{type}/{id}
+
** Refresh = GET + Header(Refresh)
+
** {id} is currently defined as a string
+
** e.g. http://localhost:8080/persistence/ExamplePU/entity/Foo/1
+
* Produces: JSON or XML
+
* Response
+
** OK, Payload: Entity
+
** NOT_FOUND if Entity does not exist
+
 
+
 
+
'''EntityManager.find API:'''
+
<pre>
+
public <T> T find(Class<T> entityClass, Object primaryKey);
+
public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties);
+
</pre>
+
 
+
The JPA-RS GET with format
+
 
+
==== Composite Key ====
+
 
+
Composite keys are supported.  In the initial implementation, the character '+' will be reserved and not available for use in fields that represent keys.  Composite keys will be separated using the '+' character and should be specified in an order cooresponding to java default sorting of the attribute names.
+
 
+
Imagine this entity:
+
 
+
* Foo
+
** idB=1
+
** idA=2
+
 
+
The URL to find this entity would be as follows:
+
 
+
http://localhost:8080/persistence/ExamplePU/entity/Foo/2+1
+
 
+
Notice that the '2' comes before the '1' because "idA" comes before "idB" when sorted in java.
+
 
+
==== Result Caching ====
+
 
+
Default EclipseLink and HTTP caching will be enabled and configured through standard means.
+
 
+
==== REFRESH ====
+
 
+
The EntityManager.refresh operation can be invoked using the find with the query hint for Refresh.  See the Query section of the document for how to use hints.
+
 
+
==== Attributes ====
+
 
+
Navigating into the attributes of an Entity (e.g. get the Address entity associated with a particular employee in a single URL) will  not be supported in the first release.
+
 
+
=== PERSIST ===
+
 
+
* PUT /persistence/{unit-name}/entity/{type}
+
** e.g. PUT http://localhost:8080/persistence/ExamplePU/entity/Foo
+
* Consumes: JSON or XML
+
* Payload: Entity
+
* Produces: JSON or XML
+
* Response:
+
** Payload: Entity returned by Persist
+
 
+
PUT is required to be itempotent.  As a result, it will fail if called with an object that expects the server to provide an ID field.  Typically this will occur if the metadata specifies a generated key and the field that contains that key is unpopulated.
+
 
+
=== MERGE ===
+
 
+
* POST /persistence/{unit-name}/entity/{type}
+
** e.g. POST http://localhost:8080/persistence/ExamplePU/entity/Foo
+
* Consumes: JSON or XML
+
* Payload: Entity
+
* Produces: JSON or XML
+
* Response:
+
** Payload: Entity returned by Merge
+
 
+
 
+
=== DELETE ===
+
 
+
* DELETE /persistence/{unit-name}/entity/{type}{id}
+
** {id} is currently defined using HTTP query parameters
+
** e.g. http://localhost:8080/persistence/ExamplePU/entity/Foo?id=1
+
* Response
+
** OK
+
 
+
== Query Operations ==
+
 
+
* Named Query: GET /persistence/{unit-name}/query/{name}{params}
+
 
+
=== Named Query ===
+
 
+
Named queries doing reads can be run two ways in JPA.  Both are supported in the REST API.
+
 
+
==== List of Results ====
+
 
+
* GET /persistence/{unit-name}/query/{name}<parameters><hints>
+
** <parameters> are specified using HTTP matrix parameters
+
** <hints> are specified using HTTP query parameters and with the key being the name of the EclipseLink query hint
+
** e.g. http://localhost:8080/persistence/ExamplePU/query/Foo.findByName;name=myname
+
** e.g. http://localhost:8080/persistence/ExamplePU/query/Foo.findByName;name=myname?eclipselink.jdbc.max-results=500
+
* Produces: JSON or XML
+
* Response
+
** Payload: List of Entities
+
 
+
==== Single Result ====
+
 
+
* GET /persistence/{unit-name}/querySingleResult/{name}<parameters><hints>
+
** <parameters> are specified using HTTP matrix parameters
+
** <hints> are specified using HTTP query parameters and with the key being the name of the EclipseLink query hint
+
** e.g. http://localhost:8080/persistence/ExamplePU/querySingleResult/Foo.findByName;name=myname
+
* Produces: JSON or XML
+
* Response
+
** Payload: Entity
+
 
+
==== Update/Delete Query ====
+
 
+
* POST /persistence/{unit-name}/query/{name}<parameters><hints>
+
** <parameters> are specified using HTTP matrix parameters
+
** <hints> are specified using HTTP query parameters and with the key being the name of the EclipseLink query hint
+
** e.g. http://localhost:8080/persistence/ExamplePU/query/Foo.deleteAllByName;name=myname
+
** e.g. http://localhost:8080/persistence/ExamplePU/query/Foo.updateName;name=myname?eclipselink.jdbc.max-results=500
+
* Produces: application/octet-stream
+
* Response
+
** Payload:  Number of entities updated or deleted in
+
 
+
== Session Beans ==
+
 
+
A restful API is provided to make a call into a SessionBean
+
 
+
* POST /persistence
+
** payload: A SessionBeanCall JSON or XML object containing the following information:
+
*** jndiName: The JNDI name of the SessionBean
+
*** methodName: The name of the SessionBean method to call
+
*** context: (optional) The JPA RS context to use to unmarshall parameters and marshall return values.  If not provided, return values are returned as is
+
*** parameters: A list of parameter Object with the following information
+
**** typeName: The name of the java class of the parameter, or the name of the entity type in JPA RS
+
**** value: The value of the parameter
+
* Produces: */*
+
* Response
+
** NOT_FOUND is the context is not found or the Session bean cannot be found on lookup
+
** OK on succes with:
+
*** Payload: Return value of SessionBean method
+
 
+
== Metadata Operations ==
+
 
+
* GET /persistence
+
* GET /persistence/{unit-name}/metadata
+
* GET /persistence/{unit-name}/metadata/entity/{type}
+
* GET /persistence/{unit-name}/metadata/query
+
 
+
=== List Existing Persistence Units ===
+
 
+
* GET /persistence
+
** e.g. http://localhost:8080/persistence/
+
* Produces: JSON
+
* Response
+
** Payload: List of Persistence Unit Names and links to metadata about them
+
 
+
Example:
+
<pre>
+
[
+
  {
+
    "rel": "auction",
+
    "type": "application/json",
+
    "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/metadata"
+
  },
+
  {
+
    "rel": "DataAppLibraryPU",
+
    "type": "application/json",
+
    "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/DataAppLibraryPU/metadata"
+
  }
+
]
+
</pre>
+
 
+
=== List Types in a Persistence Unit ===
+
 
+
* GET /persistence/{unit-name}/metadata
+
** e.g. http://localhost:8080/persistence/ExamplePU/metadata
+
* Produces: JSON
+
* Response
+
** OK, Payload: List of Types with links to more detailed metadata
+
** NOT_FOUND if persistence unit is not found
+
 
+
Example:
+
<pre>
+
{
+
  "persistence-unit-name": "auction",
+
  "types": [
+
    {
+
      "rel": "User",
+
      "type": "application/json",
+
      "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/metadata/entity/User"
+
    },
+
    {
+
      "rel": "Auction",
+
      "type": "application/json",
+
      "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/metadata/entity/Auction"
+
    },
+
    {
+
      "rel": "Bid",
+
      "type": "application/json",
+
      "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/metadata/entity/Bid"
+
    }
+
  ]
+
}
+
</pre>
+
 
+
=== List Queries in a Persistence Unit ===
+
 
+
* GET /persistence/{unit-name}/metadata/query
+
** e.g. http://localhost:8080/persistence/ExamplePU/metadata/query
+
* Produces: JSON
+
* Response
+
** OK, Payload: List of all available queries
+
** NOT_FOUND if persistence unit is not found
+
 
+
Example:
+
<pre>
+
[
+
  {
+
    "query-name": "User.all",
+
    "reference-type": "jpars.app.auction.model.User",
+
    "jpql": "SELECT u from User u",
+
    "link-template": {
+
      "rel": "execute",
+
      "method": "get",
+
      "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/query/User.all/{parameters}"
+
    }
+
  },
+
  {
+
    "query-name": "User.updateName",
+
    "reference-type": "jpars.app.auction.model.User",
+
    "jpql": "UPDATE User u SET u.name = :name WHERE u.id = :id",
+
    "link-template": {
+
      "rel": "execute",
+
      "method": "post",
+
      "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/query/User.updateName/{parameters}"
+
    }
+
  }
+
]
+
</pre>
+
 
+
=== Describe a specific entity ===
+
 
+
* GET /persistence/{unit-name}/metadata/entity/<type>
+
** e.g. http://localhost:8080/persistence/ExamplePU/metadata/entity/MyEntity
+
* Produces: JSON
+
* Response
+
** OK, Payload: Details about the entity and available operations on it
+
** NOT_FOUND if persistence unit is not found
+
 
+
Example:
+
<pre>
+
{
+
  "name": "User",
+
  "type": "jpars.app.auction.model.User",
+
  "link-templates": [
+
    {
+
      "rel": "find",
+
      "type": "application/json",
+
      "method": "get",
+
      "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/entity/Bid/{primaryKey}"
+
    },
+
    {
+
      "rel": "persist",
+
      "type": "application/json",
+
      "method": "put",
+
      "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/entity/Bid"
+
    },
+
    {
+
      "rel": "update",
+
      "type": "application/json",
+
      "method": "post",
+
      "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/entity/Bid"
+
    },
+
    {
+
      "rel": "delete",
+
      "type": "application/json",
+
      "method": "delete",
+
      "href": "http://localhost:8080/HenleyAvatarModel/jpa-rs/auction/entity/Bid/{primaryKey}"
+
    }
+
  ],
+
  "attributes": [
+
    {
+
      "name": "id",
+
      "type": "java.lang.Integer"
+
    },
+
    {
+
      "name": "name",
+
      "type": "java.lang.String"
+
    }
+
  ],
+
  "queries": [
+
    {
+
      "query-name": "User.all",
+
      "reference-type": "jpars.app.auction.model.User",
+
      "jpql": "SELECT u from User u"
+
    },
+
    {
+
      "query-name": "User.updateName",
+
      "reference-type": "jpars.app.auction.model.User",
+
      "jpql": "UPDATE User u SET u.name = :name WHERE u.id = :id"
+
    }
+
  ]
+
}
+
</pre>
+
 
+
== Admin Operations ==
+
 
+
The admin operations, which can be disabled in a given deployment, focus on persistence unit level administration operations:
+
 
+
* Create
+
** Dynamic Persistence Unit: PUT: /persistence/{unit-name}
+
** Add Dynamic Type: POST: /persistence/{unit-name}/
+
* Delete Persistence Unit: DELETE /persistence/{unit-name}
+
 
+
=== Create Dynamic Persistence Unit ===
+
 
+
Creating a dynamic persistence unit within a JPA-RS runtime involves passing in the necessary EclipseLink metadata and having the PU realized using dynamic entities.
+
 
+
* PUT /persistence/{unit-name}
+
** e.g. PUT http://localhost:8080/persistence/ExamplePU
+
* Payload: Key/Value or XML - pick one
+
** persistenceXmlURL = <URL>
+
** full text of persistence.xml
+
* Response:
+
** CREATED - on success
+
** NOT_FOUND - on failure
+
 
+
=== Bootstrap from stored Persistence Units ===
+
 
+
This option will start the REST service, look in a datastore for any stored persistence units and make those available.  Any persistence units that are bootstrapped will be added to the datastore
+
 
+
* PUT /persistence
+
** e.g. PUT http://localhost:8080/persistence
+
* Payload: Key/Value
+
** datasourceName = DatasourceJNDIName
+
* Response:
+
** CREATED - on success
+
** NOT_FOUND - on failure
+
 
+
=== Delete Persistence Unit ===
+
 
+
* DELETE /persistence/{unit-name}
+
** e.g. DELETE http://localhost:8080/persistence/ExamplePU
+
* Produces: JSON
+
* Response
+
** OK
+
 
+
==Using JPA-RS==
+
 
+
JPA-RS comes packaged in a single Jar.  The key piece of functionality is a web service found in a class called: org.eclipse.persistence.jpa.rs.Service.  Although there is no reason the web service cannot be enabled on any fairly recent container, initial development and testing was done on GlassFish 3.1.2 and this description will assume you are using GlassFish 3.1.2 or better.
+
 
+
===Packaging===
+
 
+
In order to deploy the JPA RS, it must be somehow deployed as part of your web application.  The simplest way to do that is to package it in a war file.  The key things you have to include are as follows:
+
 
+
A web.xml file pointing at the web service.  Here is an example:
+
 
+
<pre>
+
<?xml version="1.0" encoding="UTF-8"?>
+
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
+
  <display-name>JPA-RS</display-name>
+
  <servlet>
+
    <servlet-name>JPA-RS Service</servlet-name>
+
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+
    <init-param>
+
      <param-name>com.sun.jersey.config.property.packages</param-name>
+
      <param-value>org.eclipse.persistence.jpa.rs</param-value>
+
    </init-param>
+
    <load-on-startup>1</load-on-startup>
+
  </servlet>
+
  <servlet-mapping>
+
    <servlet-name>JPA-RS Service</servlet-name>
+
    <url-pattern>/jpa-rs/*</url-pattern>
+
  </servlet-mapping>
+
 
+
  <!--  Security settings here -->
+
</web-app>
+
</pre>
+
 
+
A beans.xml file.  It is required, but does not need any specific contents.
+
 
+
<pre>
+
<?xml version="1.0" encoding="UTF-8"?>
+
<beans xmlns="http://java.sun.com/xml/ns/javaee"
+
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
+
</beans>
+
</pre>
+
 
+
===Defining Metadata===
+
 
+
JPA-RS can make use of a standard JPA persistence unit, or can be bootstrapped with dynamically provided metadata. 
+
 
+
====Standard Persistence Unit====
+
 
+
To use a standard JPA persistence unit, simply deploy the JPA-RS jar in such a way as it can see the persistence unit. If you have done that, no further configuration or bootstrapping steps are required.  You can interact with the persistence unit through the REST API immediately.
+
 
+
====Dynamic Configuration====
+
 
+
JPA-RS can be bootstrapped dynamically with no java code or other persistence-unit-specific configuration in its deployment artifact.  There are two steps to doing that.
+
 
+
First, you must define your metadata.  The metadata is provided using two JPA-standard configuration files.
+
 
+
1. persistence.xml - The persistence.xml configures the persistence unit.  You will provide two properties that allow the dynamic functionality to work in this file:
+
* <property name="eclipselink.metadata-source" value="XML"/>
+
* <property name="eclipselink.metadata-source.xml.url" value="**URL OF YOUR ORM.XML FILE(("/>
+
 
+
Note: ''This persistence.xml is fairly standard except that you must keep in mind the fact that the only deployment artifacts you have are it and the orm.xml file described below, so elements like <mapping-file> and <jar-file> will only work if you find a way to make them available on the classpath.  In addition''
+
 
+
2. orm.xml - This defines the structure of your data both in the database and as it is passed through REST.  It is an extended version of the orm.xml file defined in the JPA specification.  The key extension you will use is the ability to map an entity with access type= VIRTUAL.  The VIRTUAL access type is what allows EclipseLink to build a persistence unit without java classes.  You will have to make your orm.xml available through a URL so it can be referred to in the persistence.xml as described above.
+
 
+
Here are some sample files:
+
 
+
The following persistence.xml points at a single dynamic class called Auction and gives a URL for where the mappings are defined.
+
 
+
<pre>
+
    <persistence-unit name="auction" transaction-type="JTA">
+
 
+
        <provider>
+
            org.eclipse.persistence.jpa.PersistenceProvider
+
        </provider>
+
        <jta-data-source>jdbc/dataapp</jta-data-source>
+
        <class>jpars.app.auction.model.Auction</class>
+
        <properties>
+
        <property name="eclipselink.metadata-source" value="XML"/>
+
        <property name="eclipselink.metadata-source.xml.url" value="http://localhost:8080/JPA-RS-Auction/xmlconfig/auction-orm.xml"/>
+
            <property name="eclipselink.target-server" value="SunAS9"/>
+
        </properties>
+
    </persistence-unit>
+
</persistence>
+
</pre>
+
 
+
The following orm.xml defines the mappings for a single Entity called Auction and additionally provides a query that can retrieve all the Auctions.
+
 
+
<pre>
+
<?xml version="1.0" encoding="UTF-8"?>
+
<entity-mappings version="2.3"
+
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/orm"
+
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+
    <package>jpars.app.auction.model</package>
+
 
+
    <named-query name="Auction.all">
+
        <query>SELECT a FROM Auction a</query>
+
    </named-query>
+
 
+
   
+
    <entity class="Auction" access="VIRTUAL">
+
        <table name="AUCTION_AUCTION" />
+
        <attributes>
+
            <id name="id" attribute-type="Integer">
+
                <column name="ID" />
+
                <generated-value/>
+
            </id>
+
            <basic name="name" attribute-type="String" />
+
            <basic name="image" attribute-type="String"/>
+
            <basic name="description" attribute-type="String">
+
                <column column-definition="CLOB"/>
+
            </basic>
+
            <basic name="startPrice" attribute-type="Double"/>
+
            <basic name="endPrice" attribute-type="Double"/>
+
            <basic name="sold" attribute-type="boolean"/>
+
        </attributes>
+
    </entity>
+
       
+
</entity-mappings>
+
 
+
</pre>
+
 
+
When you have provided the configuration files, you can bootstrap the persistence unit with the Bootstrap REST call documented above.
+

Latest revision as of 09:22, 9 April 2013

JPA-RS API Documentation version 2.4.0