A Brief Overview of New JPA-RS Features in EclipseLink

    EclipseLink is an open source ORM framework developed by the Eclipse Foundation. At the end of the year, release 2.6.0 is planned. project. In anticipation of this, I want to introduce you to some of the new features of the JPA-RS service, which is part of EclipseLink.
    JPA-RS allows you to automatically generate RESTful services based on the model provided by the JPA user. Moreover, practically no additional work is required from the user.


    Service version in URL


    From version to version, it is possible to change the semantics of the data transfer protocol. The JSON scheme of resources in version 2.0 of the service differs from the JSON scheme used in the previous version. To ensure compatibility, JPA-RS can return data using old formats. For this, the protocol version is rendered in the URL.

    The base URL looks like this:
    http(s)://{server:port}/{app}/persistence/{version}/{persistent-unit}/...

    Where:
    • server: port - server address and port.
    • app is the name of your application (context root).
    • persistence - The entry point to the JPA-RS application. Constant.
    • version - Optional. JPA-RS version. Currently supported values ​​are “v1.0” (previous version), “v2.0” (new version) and “latest” (latest version).
    • persistent-unit - The name of the persistent unit as specified in persistence.xml.

    The version of the service may be skipped, in this case the value “v1.0” is used.

    Examples:

    Requesting a Car object with primary key 1 from the persistence of the car-pu unit using protocol semantics version 1.0:
    http(s)://localhost:8080/jpars-test/persistence/car-pu/entity/Car/1

    Same:
    http(s)://localhost:8080/jpars-test/persistence/v1.0/car-pu/entity/Car/1

    Request the same data, but using the semantics of service version 2.0:
    http(s)://localhost:8080/jpars-test/persistence/v2.0/car-pu/entity/Car/1

    Since the latest version is currently 2.0, the following query will return the same as the previous one:
    http(s)://localhost:8080/jpars-test/persistence/latest/car-pu/entity/Car/1

    Further, solely for readability, I will designate http (s): // localhost: 8080 / jpars-test / persistence / v2.0 as {root} .

    Pagination


    JPARS allows you to split long lists into pages and return only a user-selected page. This works for queries (Named Query) and for fields of type Collection.
    Pagination is the only JPA-RS feature that requires configuration. This is done using annotations.

    Consider the following example:
    @Entity
    @Table(name = "CAR")
    @NamedQueries({
            @NamedQuery(
                    name = "Car.findAll",
                    query = "SELECT c FROM Car c ORDER BY c.name"),
            @NamedQuery(
                    name = "Car.findAllPageable",
                    query = "SELECT c FROM Car c ORDER BY c.name"),
    })
    @RestPageableQueries({
            @RestPageableQuery(queryName = "Car.findAllPageable", limit = 20)
    })
    public class Car {	
        @Id
        @Column(name = "CAR_ID")
        private Integer id;
        @Column(name = "CAR_NAME")
        private String name;
        @Column(name = "CAR_SHORT_DESCR")
        private String shortDescr;
        @Column(name = "CAR_LONG_DESCR")
        private String longDescr;
    	// Getters and setters are skipped	
    }
    


    This is a standard entity class with two JPARS annotations.
    @RestPageableQueries({
            @RestPageableQuery(queryName = "Car.findAllPageable", limit = 20)
    })
    

    This piece of code says that the Car.findAllPageable request should be issued through the RESTful service page by page with a page size of 20 entries.
    To configure pagination during a call, two parameters are used:
    • limit - page size. Cannot be higher than the limit value specified in the @RestPageableQuery annotation . It is the default value.
    • offset - offset from the beginning of the list. The sequence number of the first returned record. The default value is 0.

    For example, the following queries are similar:
    GET /query/Car.findAllPageable
    GET /query/Car.findAllPageable?limit=20&offset=0
    

    and will return the following JSON:
    {
        "items": [
            {
                "id": 1,
                "name": "Mazda MX-5",
                ...
            },
    	... <еще 19 записей> ...
        ],
        "hasMore": true,
        "limit": 20,
        "offset": 0,
        "count": 20,
        "links": [
            {
                "rel": "self",
                "href": "{root}/query/Car.findAllPageable"
            }
            {
                "rel": "canonical",
                "href": "{root}/query/Car.findAllPageable"
            }
            {
                "rel": "next",
                "href": "{root}/query/Car.findAllPageable?offset=20"
            }
        ]
    }
    

    The server response also contains additional information:
    • hasMore - true if this page is not the last.
    • limit - page size used by the server.
    • offset is the offset used.
    • count - the number of entries on the page.
    • next link - URL of the next page (if available)
    • link prev - URL of the previous page (if available)

    Second page:
    {root}/query/Car.findAllPageable?offset=20

    or
    {root]/query/Car.findAllPageable?limit=20&offset=20

    Third page of 10 entries:
    {root}/query/Car.findAllPageable?limit=10&offset=20

    7 entries starting from the third:
    {root}/query/Car.findAllPageable?limit=7&offset=3


    The server always uses the minimum limit specified in the request and used in the annotation. I.e
    {root}/query/Car.findAllPageable?limit=100

    will only return 20 records. In this case, the limit value in the server response will be 20, as in the example above.

    Field filtering


    When requesting data, sometimes it is necessary to return not the entire record, but only some fields. For example, the longDescr field of the Car class contains text of an impressive size, which we do not want to transmit over the network. This is what field filtering is for.

    Filtering is configured by two parameters:
    • fields - a list of fields returned by the server. Fields are separated by commas.
    • excludeFields - a list of fields that are not returned by the server.

    For example:
    GET {root}/entity/Car/1?fields=id,name,shortDescr

    Only the id , name, and shortDescr fields of the Car class will be returned :
    {
        "id": 1,
        "name": "Mazda MX-5",
        "shortDescr": "двухместный родстер",
        ...
    }
    

    The same query will return the same:
    GET {root}/entity/Car/1?excludeFields=longDescr

    If you try to use both fields and excludeFields in the same request, the server will return an error.

    Metadata


    Metadata contains additional information about the resource. In our case, this is a link to the JSON resource scheme and base URL.

    For example, metadata for our Car class looks like this:
    {
        "name": "Car",
        "links": [
            {
                "rel": "alternate",
                "href": "/metadata-catalog/entity/Car",
                "mediaType": "application/schema+json"
            },
            {
                "rel": "canonical",
                "href": "/metadata-catalog/entity/Car",
                "mediaType": "application/json"
            },
            {
                "rel": "describes",
                "href": "{root}/entity/Car"
            }
        ]
    }

    You can get metadata for the Car class like this:
    {root}/metadata-catalog/entity/Car

    To query Car.findAll like this:
    {root}/metadata-catalog/query/Car.findAll

    Another way to get metadata is to call the OPTIONS method on the base URL of the resource.
    OPTIONS /entity/Basket

    The server will return a link to the metadata in the Link header with rel = "describedby"
    Link: ; rel="describedby"


    Resource catalog


    The resource catalog, like the table of contents of a book, contains metadata for all available resources. This is the right place to start working with an unfamiliar service. Here you can always see the available objects and requests.

    The resource catalog is available at the following address:
    GET /metadata-catalog

    Server response:
    {
        "items": [
            {
                "name": "Car",
                ...
            },
            {
                "name": "Car.findAll",
                ...
            },
            {
                "name": "Car.findAllPageable",
                ...
            }
        ],
        "links": [
            {
                "rel": "canonical",
                "href": "/metadata-catalog"
            }
        ]
    }

    I trimmed the server response solely for readability. The medadata for all resources looks the same as indicated at the beginning of the chapter.

    JSON Resource Schema


    Although the JSON scheme is not yet an approved standard and work on the specification has not yet been completed, JPS-RS already partially supports draft 4 specifications. This concerns the output format of objects and the list of objects.

    The resource metadata URL can also be used to get its (resource) JSON schema. To do this, we perform an HTTP GET request using the media type “application / schema + json”. That is, setting the “accept” value in the HTTP header to “application / schema + json”.

    Getting the schema for the Car class:
    GET /metadata-catalog/entity/Car HTTP/1.1
    Accept-Encoding: gzip,deflate
    accept: application/schema+json
    Host: 
    Proxy-Connection: Keep-Alive
    User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
    

    Server response:
    {
        "$schema": "/metadata-catalog/entity/Car#",
        "allOf": [
            {
                "$ref": "rest-schemas/#/singularResource"
            }
        ],
        "title": "Car",
        "properties": {
            "id": {
                "type": "number"
            },
            "name": {
                "type": "string"
            },
            "shortDescr": {
                "type": "string"
            },
            "longDescr": {
                "type": "string"
            },
        },
        "links": [
            {
                "rel": "describedby",
                "href": "/entity/Car"
            },
            {
                "rel": "find",
                "href": "{root}/entity/Car/{primaryKey}",
                "method": "GET"
            },
            {
                "rel": "create",
                "href": "{root}/entity/Car",
                "method": "PUT"
            },
            {
                "rel": "update",
                "href": "{root}/entity/Car",
                "method": "POST"
            },
            {
                "rel": "delete",
                "href": "{root}/entity/Car/{primaryKey}",
                "method": "DELETE"
            }
        ]
    }

    That's all. I hope you enjoyed the review.

    Nightly builds EclipseLink can be downloaded here:
    www.eclipse.org/eclipselink/downloads/nightly.php

    Also popular now: