Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

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

(Relationships)
 
(24 intermediate revisions by 2 users not shown)
Line 1: Line 1:
__TOC__
+
[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.
+
 
+
==Using JPA-RS==
+
 
+
JPA-RS comes in two parts and is available starting with our 2.4.0 release.
+
 
+
# Server side - The majority of code is written in EclipseLink and should be run server-side there are two ways you can get this functionality
+
## If you are on a server that distributes with the full EclipseLink jar or on our 2.4.0 or 2.4.1 release, no changes are needed
+
## If you are on our 2.4.2 or a later release and on a server that uses our OSGi bundles, you will have to ensure that you have the org.eclipse.persistence.dbws bundle (e.g. GlassFish ships with the OSGi bundles.  You will need to ensure our org.eclipse.persistence.dbws bundle is available to GlassFish)
+
# Client Side - A small web-fragment called org.eclipse.persistence.jpars is used to enable JPA-RS.  This web-fragment should be included with your application.
+
 
+
Although there is no reason the web service cannot be enabled on any fairly recent container that contains Jersey, 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.
+
 
+
JPA-RS uses "/persistence" as its path.  (i.e. The base URL for JPA-RS for a particular application is: http://<server>:<port>/<applicationName>/persistence)
+
 
+
== 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 given JPA-RS application context the following high level operations are available.
+
 
+
* BASE /persistence/{unit-name}
+
* ENTITY: /persistence/{unit-name}/entity
+
* QUERY: /persistence/{unit-name}/query
+
* SINGLE_RESULT_QUERY: /persistence/{unit-name}/singleResultQuery
+
* METADATA: /persistence/{unit-name}/metadata
+
 
+
===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).
+
NOTE: In many REST utilities the accept value is defaulted to application/xml making it the users responsibility to configure this value explicitly.
+
 
+
=== Logging ===
+
 
+
Messages related to JPA-RS operations are logged to a logger called org.eclipse.persistence.jpars.  Most messages are logged at the FINE level.  Exception stacks are logged at FINER.
+
 
+
Messages related to operations withing EntityManagers, EntityManagerFactories and JAXBContexts are logged in the same manner as other EclipseLink logging.
+
 
+
=== Deployment Requirements ===
+
 
+
Weaving is required for several features to work (providing relationships as links, editing relationships, dealing with LAZY x-1 relationships).  You should either deploy to a Java EE compliant server or statically weave you classes.
+
 
+
Note: Lazy x-1 relationships are only supported when using JPA RS's default mapping strategy which returns those mappings as links by using read-only mappings for the JSON/XML support and providing the links through a weaved attribute.
+
 
+
== 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>
+
 
+
 
+
==== 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) is initially only supported to one level:
+
 
+
e.g. /persistence/{unit-name}/entity/{type}/{id}/{relationship} will work
+
 
+
/persistence/{unit-name}/entity/{type}/{id}/{relationship}/{index}/{relationship2} will not
+
 
+
 
+
 
+
=== 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
+
 
+
== Entity operations on relationships: /persistence/{unit-name}/entity/{entity}/{id}/{relationship} ==
+
 
+
=== READ ===
+
 
+
To get the values of a relationship
+
 
+
* GET /persistence/{unit-name}/entity/{type}/{id}/{relationship}
+
** {id} is currently defined as a string
+
** {relationship} is the JPA name of the relationship
+
** e.g. http://localhost:8080/persistence/ExamplePU/entity/Foo/1/myRelationship
+
* Produces: JSON or XML
+
* Response
+
** OK, Payload: Entity or List of Entities
+
** NOT_FOUND if Entity does not exist
+
 
+
=== ADD ===
+
 
+
To add to a list or replace the value of a X-1 Relationship
+
 
+
* POST /persistence/{unit-name}/entity/{type}/{id}/{relationship}{partner}
+
** For unidirectional relationships {partner} is not required
+
*** e.g. POST http://localhost:8080/persistence/ExamplePU/entity/Foo/1/myRelationship
+
** For bi-directional relationships, you must provide the name of the attribute that makes up the opposite side of the relationship.
+
*** e.g. To update an Auction.bid where the opposite side of the relationship is Bid.auction use the following:
+
**** e.g. POST http://localhost:8080/persistence/ExamplePU/entity/Foo/1/myRelationship;partner=bid
+
* Consumes: JSON or XML
+
* Payload: Entity with the new value
+
* Produces: JSON or XML
+
* Response:
+
** Payload: Entity with added element
+
 
+
=== REMOVE ===
+
 
+
To remove a specific entity from the list or null on an X-1 Relationship
+
 
+
* DELETE /persistence/{unit-name}/entity/{type}/{id}/{relationship}
+
** e.g. DELETE http://localhost:8080/persistence/ExamplePU/entity/Foo/1/myRelationship
+
* Consumes: JSON or XML
+
* Payload: Entity to be removed
+
* Response
+
** OK
+
** Payload: Entity with added element
+
 
+
== 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
+
 
+
==== 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
+
 
+
== Single Result Queries ==
+
 
+
==== 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
+
 
+
== Base Operations ==
+
 
+
=== 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>
+
 
+
=== Call a Session Bean ===
+
 
+
* 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/{unit-name}/metadata
+
* GET /persistence/{unit-name}/metadata/entity/{type}
+
* GET /persistence/{unit-name}/metadata/query
+
 
+
=== 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>
+
 
+
== Entity Representation ==
+
 
+
Entities in JPA-RS are represented in two ways.
+
 
+
# As JPA Entities - The mappings the JPA Entities must be represented in the typical EclipseLink JPA fashion using either annotations or xml files.  These mappings will be used to interact with the data source.
+
# As JAXB/JSON - No specific mapping information is required here.  By default, JPA-RS will use the JAXB-specification-defined defaults to map to JAXB/JSON.  (EclipseLink's JAXB implementation provides a JSON option that we leverage here).  You can optionally provide JAXB annotations on the classes to alter the way the objects are mapped.  Additionally, the persistence unit property "eclipselink.jpa-rs.oxm" can be specified in your persistence unit's persistence.xml to specify xml-defined JAXB mappings.
+
 
+
=== Relationships ===
+
 
+
In general, JAXB default mappings are sufficient to allow information exchange using JSON/JAXB.  There are, however, some special cases when dealing with relationships.
+
 
+
==== Bi-directional Relationships and Cycles ====
+
 
+
Bi-directional Relationships are quite typical in JPA and easy to represent in a database using foreign keys.  They are much more difficult to represent in a default manner in a JSON or XML document using JAXB.  EclipseLink's JAXB implementation provides a way to define an inverse relationship.  Inverse relationships are not specifically written to XML/JSON, but are populated when the JSON/XML is unmarshalled.  EclipseLink takes advantage of that functionality when it finds bidirectional relationships as follows:
+
 
+
* JPA bidirectional relationships are defined to have an owning side and a non-owning side.  JPA mapping provides a "mapped-by" attribute that defines which is which.  JPA-RS defaults the owning side to be an inverse relationship.  As a result, when an object with a owned-relationship is read or written that relationship will be ignored.
+
 
+
Imagine the following pseudo code:
+
 
+
<pre>
+
@Entity
+
ClassA{
+
 
+
@Id
+
int id
+
 
+
@OneToOne(mappedby="myA")
+
myB
+
 
+
}
+
 
+
 
+
@Entity
+
ClassB{
+
 
+
@Id
+
int id
+
 
+
@OneToOne
+
myA
+
 
+
}
+
 
+
</pre>
+
 
+
Imagine:
+
 
+
* ''A1 with id=1 and myB = B1''
+
* ''B1 with id=11 and myA = A1''
+
 
+
The following JSON cooresponds to those objects:
+
 
+
<pre>
+
 
+
A {
+
  id:1
+
}
+
 
+
B {
+
  id:11
+
  myA {
+
    id: 1
+
  }
+
}
+
 
+
 
+
</pre>
+
 
+
Cycles are more difficult to detect and JPA-RS requires that users provide JAXB mapping that allows us to resolve cycles.
+
 
+
==== Pass By Reference/ Pass By Value ====
+
 
+
JPA-RS allows relationship objects to be passed either by value or by reference. 
+
 
+
To pass an object by value, simply create typical JSON/XML that represents the object.  The following JSON passes myA by value:
+
 
+
<pre>
+
B {
+
  id:11
+
  myA {
+
    id: 1
+
  }
+
}
+
</pre>
+
 
+
To pass an object by reference, a link is used.  The link represents the JPA-RS call necessary to get that object.  The following JSON passes myA by reference:
+
 
+
<pre>
+
B {
+
  id:11
+
  myA {
+
    _link:{
+
        href: "http://localhost:8080/app/persistence/pu/entity/A/1"
+
        method: "GET"
+
        rel: "self"
+
    }
+
  }
+
}
+
</pre>
+
 
+
Lists can mix and match items represented by reference and by value.
+
 
+
JPA-RS read operations currently return all relationships by reference.
+
 
+
=== Sample JSON ===
+
 
+
The following provides some sample JSON that can be sent to JPA-RS in a regular-expression-like syntax.
+
 
+
<pre>
+
<code>
+
{
+
  numericAttribute: 1
+
  stringAttribute: "auction1"
+
  dateAttribute: 12-09-16
+
  singleRelatedItem: RELATED_ITEM?
+
    listRelatedItem:
+
    {
+
        RELATED_ITEM*
+
    }
+
}
+
 
+
 
+
RELATED_ITEM =
+
 
+
    {
+
      numericAttribute: 11
+
      stringAttribute: "myName"
+
    }
+
 
+
OR
+
 
+
    _link {
+
      rel="self",
+
      href=LINK_HREF,
+
      method="GET"
+
    }
+
</pre>
+
 
+
=== XML ===
+
 
+
XML representation mimics the JSON representation.  Sample XML and Schemas to come.
+
 
+
== Future Development ==
+
 
+
http://wiki.eclipse.org/EclipseLink/Development/JPA-RS
+
 
+
=== 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>
+

Latest revision as of 10:22, 9 April 2013

JPA-RS API Documentation version 2.4.0

Back to the top