EclipseLink — это ORM фрэймворк с открытым исходным кодом, разрабатываемый Eclipse Foundation. В конце года запланирован выход версии 2.6.0. проекта. В преддверии этого, я хочу ознакомить вас с некоторыми новыми возможностями службы JPA-RS, которая является частью EclipseLink.
JPA-RS позволяет автоматически генерировать RESTful сервисы на базе предоставленной пользователем JPA модели. При этом практически никакой дополнительной работы от пользователя не требуется.
От версии к версии возможно изменение семантики протокола передачи данных. JSON-cхема ресурсов в версии 2.0 сервиса отличается от JSON-схемы, используемой в предыдущей версии. Для обеспечения совместимости JPA-RS умеет возвращать данные используя старые форматы. Для этого версия протокола вынесена в URL.
Базовый URL выглядит так:
Где:
Версия сервиса может быть пропущена, в этом случае используется значение «v1.0».
Примеры:
Запрос объекта Car с примарным ключом 1 из персистент юнита car-pu используя семантику протокола версии 1.0:
Тоже самое:
Запрос тех же данных, но используя семантику сервиса версии 2.0:
Так как последняя версия на данный момент 2.0, то следующий запрос вернет тоже самое, что и предыдущий:
Далее, исключительно для читабельности, я обозначу http(s)://localhost:8080/jpars-test/persistence/v2.0 как {root}.
JPARS позволяет разделять на страницы длинные списки и возвращать только выбранную пользователем страницу. Это работает для запросов (Named Query) и для полей типа Collection.
Постраничный вывод — это единственная функция JPA-RS, требующая конфигурации. Это делается с помощью аннотаций.
Рассмотрим следующий пример:
Это стандартный entity класс c двумя JPARS аннотациями.
Этот кусок кода говорит, что запрос Car.findAllPageable должен выдаваться через RESTful сервис постранично с размером страницы — 20 записей.
Для конфигурации постраничного вывода при вызове используются два параметра:
К примеру, следующие запросы аналогичны:
и вернут следующий JSON:
Ответ сервера также содержит дополнительную информацию:
Вторая страница:
или
Третья страница размером 10 записей:
7 записей начиная с третей:
Сервер всегда использует минимальный limit из укзанного в запросе и используемного в аннотиции. То есть
вернет только 20 записей. При этом значение limit в ответе сервера будет 20, как в примере выше.
При запросе данных иногда бывает нужно вернуть не всю запись, а только некоторые поля. К примеру, поле longDescr класса Car содержит текст внушительного размера, который мы не хотим передавать по сети. Именно для этого служит фильтрация полей.
Фильтрация конфигурируется двумя параметрами:
К примеру:
Вернет только поля id, name и shortDescr класса Car:
Тоже самое вернет и следующий запрос:
При попытке использовать оба параметра fields и excludeFields в одном запросе, сервер вернет ошибку.
Метаданные содержат дополнительную информацию о ресурсе. В нашем случае это ссылка на JSON схему ресурса и базовый URL.
К примеру, метаданные для нашего класса Car выглядят так:
Получить метаданные для класса Car можно вот так:
Для запроса Car.findAll так:
Еще один способ получения метаданных — вызов OPTIONS метода на базовый URL ресурса.
Сервер вернет ссылку на метаданные в заголовке Link c rel=«describedby»
Каталог ресурсов, как оглавление книги, содержит метадан��ые для всех доступных ресурсов. Это правильное место начала работы с незнакомой службой. Здесь всегда можно посмотреть доступные объкты и запросы.
Каталог ресурсов доступен по следующему адресу:
Ответ сервера:
Я урезал ответ сервера исключительно для читабельности. Медаданные для всех ресурсов выглядят также как указано в начале главы.
Хотя JSON схема пока не является утвержденным стандартом и работы над спецификацией еще не закончены, JPS-RS уже частично поддерживает draft 4 спецификации. Это касается формата вывода объектов и списка объектов.
URL метаданных ресурса можно использовать и для получения его (ресурса) JSON схемы. Для этого выполняем HTTP GET запрос используя тип медиа «application/schema+json». То есть установив в HTTP заголовке «accept» значение «application/schema+json».
Получение схемы для класса Car:
Ответ сервера:
На этом все. Надеюсь, вам понравился обзор.
Nightly builds EclipseLink можно скачать здесь:
www.eclipse.org/eclipselink/downloads/nightly.php
JPA-RS позволяет автоматически генерировать RESTful сервисы на базе предоставленной пользователем JPA модели. При этом практически никакой дополнительной работы от пользователя не требуется.
Версия сервиса в URL
От версии к версии возможно изменение семантики протокола передачи данных. JSON-cхема ресурсов в версии 2.0 сервиса отличается от JSON-схемы, используемой в предыдущей версии. Для обеспечения совместимости JPA-RS умеет возвращать данные используя старые форматы. Для этого версия протокола вынесена в URL.
Базовый URL выглядит так:
http(s)://{server:port}/{app}/persistence/{version}/{persistent-unit}/...
Где:
- server:port — адрес и порт сервера.
- app — имя вашего приложения (context root).
- persistence — Точка входа в JPA-RS приложение. Константа.
- version — Не обязательный. Версия JPA-RS. На данный момент поддерживаются значения «v1.0» (предыдущая версия), «v2.0» (новая версия) и «latest» (последняя версия).
- persistent-unit — Имя persistent unit как указано в persistence.xml.
Версия сервиса может быть пропущена, в этом случае используется значение «v1.0».
Примеры:
Запрос объекта Car с примарным ключом 1 из персистент юнита car-pu используя семантику протокола версии 1.0:
http(s)://localhost:8080/jpars-test/persistence/car-pu/entity/Car/1
Тоже самое:
http(s)://localhost:8080/jpars-test/persistence/v1.0/car-pu/entity/Car/1
Запрос тех же данных, но используя семантику сервиса версии 2.0:
http(s)://localhost:8080/jpars-test/persistence/v2.0/car-pu/entity/Car/1
Так как последняя версия на данный момент 2.0, то следующий запрос вернет тоже самое, что и предыдущий:
http(s)://localhost:8080/jpars-test/persistence/latest/car-pu/entity/Car/1
Далее, исключительно для читабельности, я обозначу http(s)://localhost:8080/jpars-test/persistence/v2.0 как {root}.
Постраничный вывод
JPARS позволяет разделять на страницы длинные списки и возвращать только выбранную пользователем страницу. Это работает для запросов (Named Query) и для полей типа Collection.
Постраничный вывод — это единственная функция JPA-RS, требующая конфигурации. Это делается с помощью аннотаций.
Рассмотрим следующий пример:
@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 }
Это стандартный entity класс c двумя JPARS аннотациями.
@RestPageableQueries({ @RestPageableQuery(queryName = "Car.findAllPageable", limit = 20) })
Этот кусок кода говорит, что запрос Car.findAllPageable должен выдаваться через RESTful сервис постранично с размером страницы — 20 записей.
Для конфигурации постраничного вывода при вызове используются два параметра:
- limit — размер страницы. Не может быть выше значения limit указанного в аннотации @RestPageableQuery. Оно же является значением по умолчанию.
- offset — смещение от начала списка. Порядковый номер первой возвращаемой записи. Значение по умолчению — 0.
К примеру, следующие запросы аналогичны:
GET <root>/query/Car.findAllPageable GET <root>/query/Car.findAllPageable?limit=20&offset=0
и вернут следующий 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" } ] }
Ответ сервера также содержит дополнительную информацию:
- hasMore — true если данная страница не является последней.
- limit — размер страницы используемый сервером.
- offset — используемое смещение.
- count — количество записей на странице.
- ссылка next — URL следующей страницы (если доступна)
- ссылка prev — URL предыдущей страницы (если доступна)
Вторая страница:
{root}/query/Car.findAllPageable?offset=20
или
{root]/query/Car.findAllPageable?limit=20&offset=20
Третья страница размером 10 записей:
{root}/query/Car.findAllPageable?limit=10&offset=20
7 записей начиная с третей:
{root}/query/Car.findAllPageable?limit=7&offset=3
Сервер всегда использует минимальный limit из укзанного в запросе и используемного в аннотиции. То есть
{root}/query/Car.findAllPageable?limit=100
вернет только 20 записей. При этом значение limit в ответе сервера будет 20, как в примере выше.
Фильтрация полей
При запросе данных иногда бывает нужно вернуть не всю запись, а только некоторые поля. К примеру, поле longDescr класса Car содержит текст внушительного размера, который мы не хотим передавать по сети. Именно для этого служит фильтрация полей.
Фильтрация конфигурируется двумя параметрами:
- fields — список полей, возвращаемых сервером. Поля разделяются запятыми.
- excludeFields — список полей, которые не возвращаются сервером.
К примеру:
GET {root}/entity/Car/1?fields=id,name,shortDescr
Вернет только поля id, name и shortDescr класса Car:
{ "id": 1, "name": "Mazda MX-5", "shortDescr": "двухместный родстер", ... }
Тоже самое вернет и следующий запрос:
GET {root}/entity/Car/1?excludeFields=longDescr
При попытке использовать оба параметра fields и excludeFields в одном запросе, сервер вернет ошибку.
Метаданные
Метаданные содержат дополнительную информацию о ресурсе. В нашем случае это ссылка на JSON схему ресурса и базовый URL.
К примеру, метаданные для нашего класса Car выглядят так:
{ "name": "Car", "links": [ { "rel": "alternate", "href": "<root>/metadata-catalog/entity/Car", "mediaType": "application/schema+json" }, { "rel": "canonical", "href": "<root>/metadata-catalog/entity/Car", "mediaType": "application/json" }, { "rel": "describes", "href": "{root}/entity/Car" } ] }
Получить метаданные для класса Car можно вот так:
{root}/metadata-catalog/entity/Car
Для запроса Car.findAll так:
{root}/metadata-catalog/query/Car.findAll
Еще один способ получения метаданных — вызов OPTIONS метода на базовый URL ресурса.
OPTIONS <root>/entity/Basket
Сервер вернет ссылку на метаданные в заголовке Link c rel=«describedby»
Link: <root/metadata-catalog/entity/Basket>; rel="describedby"
Каталог ресурсов
Каталог ресурсов, как оглавление книги, содержит метадан��ые для всех доступных ресурсов. Это правильное место начала работы с незнакомой службой. Здесь всегда можно посмотреть доступные объкты и запросы.
Каталог ресурсов доступен по следующему адресу:
GET <root>/metadata-catalog
Ответ сервера:
{ "items": [ { "name": "Car", ... }, { "name": "Car.findAll", ... }, { "name": "Car.findAllPageable", ... } ], "links": [ { "rel": "canonical", "href": "<root>/metadata-catalog" } ] }
Я урезал ответ сервера исключительно для читабельности. Медаданные для всех ресурсов выглядят также как указано в начале главы.
JSON-схема ресурсов
Хотя JSON схема пока не является утвержденным стандартом и работы над спецификацией еще не закончены, JPS-RS уже частично поддерживает draft 4 спецификации. Это касается формата вывода объектов и списка объектов.
URL метаданных ресурса можно использовать и для получения его (ресурса) JSON схемы. Для этого выполняем HTTP GET запрос используя тип медиа «application/schema+json». То есть установив в HTTP заголовке «accept» значение «application/schema+json».
Получение схемы для класса Car:
GET <root>/metadata-catalog/entity/Car HTTP/1.1 Accept-Encoding: gzip,deflate accept: application/schema+json Host: <host:port> Proxy-Connection: Keep-Alive User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Ответ сервера:
{ "$schema": "<root>/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": "<root>/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" } ] }
На этом все. Надеюсь, вам понравился обзор.
Nightly builds EclipseLink можно скачать здесь:
www.eclipse.org/eclipselink/downloads/nightly.php