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/DesignDocs/326663"

(GET - Read Operation)
(GET - Read Operation)
Line 198: Line 198:
 
We will need to differentiate between the single key case that uses path parameters and the composite key case that uses matrix parameters:
 
We will need to differentiate between the single key case that uses path parameters and the composite key case that uses matrix parameters:
  
'''Single Key'''
+
'''Single Key - Path Parameter'''
  
 
<source lang="java">
 
<source lang="java">
Line 209: Line 209:
 
</source>
 
</source>
  
Composite Key
+
'''Composite Key - Matrix Paramters'''
 +
 
 
<source lang="java">
 
<source lang="java">
 
@GET
 
@GET
Line 223: Line 224:
  
 
Error Responses:
 
Error Responses:
*
+
* ?
  
 
==== PUT - Update Operation  ====
 
==== PUT - Update Operation  ====

Revision as of 11:17, 1 October 2010

Design Specification: JPA RESTful Service

ER 326663

Document History

Date Author Version Description & Notes
2010/09/30 Blaise Doughan Initial Version

Project overview

Provide an easy means for users to expose their JPA entities through a RESTful service.

Goals:

  • Provide a means for users to implement a standards (JAX-RS/JAXB/JPA) based RESTful service with minimal code.

Concepts

The following example demonstrates how JAXB and JPA can be used to implement a RESTful (JAX-RS) web service:

Requirements

  1. Expose JPA EntityManager as RESTful (JAX-RS) service
  2. Execute CRUD operations
  3. Execute named queries
  4. Accept/Return both XML and JSON mime types
  5. Convert JPA exceptions into appropriate HTTP response codes

Design Constraints

  1. JAX-RS operates on the HTTP protocol, we will be limited by the constraints of this protocol:
    1. URI parameters are limited to simple types
    2. Error conditions are limited to response codes
    3. Operations either produce data or consume data, not both
  2. JAX-RS uses JAXB to produce JSON messages (atleast Jersey does). We are limited to the types of JSON messages that can be produced/consumed by JAXB.

Design / Functionality

URI Representation

Data in a RESTful service is referenced through URIs. The common parts of the URI for this example are:

  • http://www.example.com/customer-app/rest - the first part of the URI is based on how the application is deployed.
  • customers - this part of the path corresponds to the JAX-RS @Path annotation on the RESTful service.

URI for JPA Entities with Unary Key

If the JPA entity has a single part primary key then in the corresponding URI the primary key will be represented as a path parameter. This is a common RESTful operation.

@Entity
public class Customer implements Serializable {
 
    @Id
    private long id;
 
}

URI corresponding to Customer entity with id == 1:

URI for JPA Entities with Composite Keys

A different mechanism needs be be employed when locating a resource with composite keys. The URI will leverage the property names from the JPA key class as matrix parameters. The advantage of using matrix paramters is that they may be cached. The same representation is also used if composite keys is represented using an embedded key class.

@Entity
@IdClass(CustomerID.clsas)
public class Customer implements Serializable {
 
    @Id
    private long id;
 
    @Id
    private String country;
 
}
public class CustomerID {
 
    private String country;
    private long id;
 
    public CustomerID() {
        super();
    }
 
    public CustomerID(String country, long id) {
        this.country = country;
        this.id = id;
    }
 
    public String getCountry() {
        return country;
    }
 
    public long getId() {
        return id;
    }
 
}

URI corresponding to the instance of Customer with id == 1 and country == CA:

Named Queries

A named query call needs to be mapped to a URI. Below is an example named query:

@NamedQuery(name = "findCustomerByName",
            query = "SELECT c " +
                    "FROM Customer c " +
                    "WHERE c.firstName = :firstName AND " +
                    "      c.lastName = :lastName")

Get Single Result:

Get Result List:

URI components:

  • findCustomersByName - this corresponds to the name of the named query
  • singleResult or resultList - this portion indicates whether one or many results are returned
  •  ;firstResult=1;maxResults=10 - optional parameters to specify firstResult and maxResults
  •  ?firstName=Jane&lastName=Doe - these are the query parameters, the name of the parameter must match exactly the parameter name in the named query.

The parameters will be used to build the equivalent of the following:

Query query = entityManager.createNamedQuery("findCustomersByCity");
query.setParameter("firstName", "Jane");
query.setParameter("lastName", "Doe");
query.setFirstResult(1);
query.setMaxResults(10);
return query.getResultList();

REST (CRUD) Operations

POST - Create Operation

Using the Jersery client APIs the following is how a post operation is called on our service. The XML message will converted to the appropriate object type using JAXB.

Client c = Client.create();
WebResource resource = client.resource("http://www.example.com/customer-app/rest/customers");
ClientResponse response = resource.type("application/xml").post(ClientResponse.class, "<customer>...</customer>");
System.out.println(response);

This call will be received by

@POST
@Consumes({"application/xml", "application/json"})
public Response create(@Context UriInfo uriInfo, EntityType entity) {
    entityManager().persist(entity);
    UriBuilder uriBuilder = pkUriBuilder(uriInfo.getAbsolutePathBuilder(), entity);
    return Response.created(uriBuilder.build()).build();
}

Successful Responses

  • 200 OK
  • Return the URI for the created entity

Error Responses:

GET - Read Operation

Get is a read-only operation. It is used to query resources. The following is an example of how to invoke a GET call using the Jersey client APIs:

WebResource resource = client.resource("http://www.example.com/customer-app/rest/customers;id=1;country=CA");
ClientResponse response = resource.accept(mimeType).get(ClientResponse.class);

We will need to differentiate between the single key case that uses path parameters and the composite key case that uses matrix parameters:

Single Key - Path Parameter

@GET
@Path("{id}")
@Produces({"application/xml", "application/json"})
public EntityType read(@PathParam("id") KeyType id) {
    return entityManager().find(entityClass, id);
}

Composite Key - Matrix Paramters

@GET
@Produces({"application/xml", "application/json"})
public EntityType read(@Context UriInfo info) {
    return entityManager().find(entityClass, getPrimaryKey(info));
}

Successful Responses

  • 200 OK - If a result is returned
  • 204 No Content - If no results are returned

Error Responses:

  •  ?

PUT - Update Operation

The put operation updates the underlying resource. When using put the client knows the identity of the resource being updated.

@PUT
@Consumes(MediaType.APPLICATION_XML)
public void update(Customer customer) {
 entityManager.merge(customer);
}

Successful Responses

  • 200 OK

Error Responses:

  • 409 Conflict - Locking related exception

DELETE - Delete Operation

The delete operation is used to remove resources. It is not an error to remove a non-existent resource.

@DELETE
@Path("{id}")
public void delete(@PathParam("id") long id) {
 Customer customer = read(id);
 if (null != customer) {
 entityManager.remove(customer);
 }
}

Successful Responses

  • 200 OK

Error Responses:

Testing

API

The user would implement their JPA based JAX-RS service as follows. They would extend JPASingleKeyResource or JPACompositeKeyResource depending upon their key type.

import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.ws.rs.Path;
 
import org.eclipse.persistence.rest.JPASingleKeyResource;
 
@Stateless
@LocalBean
@Path("/customers")
public class CustomerService extends JPASingleKeyResource<Customer, Long> {
 
    @PersistenceContext(unitName="CustomerService", type=PersistenceContextType.TRANSACTION)
    EntityManager entityManager;
 
    public CustomerService() {
        super(Customer.class);
    }
 
    @Override
    protected EntityManager entityManager() {
        return entityManager;
    }
 
}

GUI

Config files

This feature does not require the creation of any new configuration files. However the user will be responsible for providing the required JAX-RS, JPA, and JAXB config files.

JAX-RS

  • The user will need to create the JAX-RS deployment artifacts appropriate to their deployment platform.

JPA

  • The user will need to create the necessary artifacts for JPA

JAXB

  • The user will need to create the necessary artifacts for JAXB
    • jaxb.properties file to specify JAXB implementation
    • eclipselink-oxm.xml as an alternate metadata representation

Documentation

Open Issues

This section lists the open issues that are still pending that must be decided prior to fully implementing this project's requirements.

Issue # Owner Description / Notes
1 Blaise Doughan When to use matrix and/or query parameters? Will consult with Paul Sandoz to resolve this issue.

Decisions

This section lists decisions made. These are intended to document the resolution of open issues or constraints added to the project that are important.

Issue # Description / Notes Decision

Future Considerations

During the research for this project the following items were identified as out of scope but are captured here as potential future enhancements. If agreed upon during the review process these should be logged in the bug system.

Back to the top