«1. Обзор

LinkRest — это платформа с открытым исходным кодом для создания управляемых данными веб-сервисов REST. Он построен на основе JAX-RS и Apache Cayenne ORM и использует протокол сообщений на основе HTTP/JSON.

По сути, этот фреймворк предназначен для предоставления простого способа представления нашего хранилища данных в Интернете.

В следующих разделах мы рассмотрим, как создать веб-службу REST для доступа к модели данных с помощью LinkRest.

2. Зависимости Maven

Чтобы начать работу с библиотекой, сначала нам нужно добавить зависимость link-rest:

<dependency>
    <groupId>com.nhl.link.rest</groupId>
    <artifactId>link-rest</artifactId>
    <version>2.9</version>
</dependency>

Это также добавляет артефакт cayenne-server.

Кроме того, мы будем использовать Jersey в качестве реализации JAX-RS, поэтому нам нужно добавить зависимость jersey-container-servlet, а также jersey-media-moxy для сериализации ответов JSON:

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.25.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-moxy</artifactId>
    <version>2.25.1</version>
</dependency>

Для в нашем примере мы будем работать с базой данных H2 в памяти, поскольку ее проще настроить; как следствие, мы также добавим h2 :

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

3. Модель данных Cayenne

Модель данных, с которой мы будем работать, содержит сущности «Отдел» и «Сотрудник», представляющие отношение «один ко многим». :

Как уже упоминалось, LinkRest работает с объектами данных, созданными с помощью Apache Cayenne ORM. Работа с Cayenne не является основной темой этой статьи, поэтому для получения дополнительной информации ознакомьтесь с документацией по Apache Cayenne.

Мы сохраним проект Cayenne в файле cayenne-linkrest-project.xml.

После запуска cayenne-maven-plugin будут сгенерированы два абстрактных класса _Department и _Employee, которые расширят класс CayenneDataObject, а также два конкретных производных от них класса, Department и Employee.

Эти последние классы мы можем настраивать и использовать с LinkRest.

4. Запуск приложения LinkRest

В следующем разделе мы собираемся написать и протестировать конечные точки REST, поэтому, чтобы иметь возможность запускать их, нам нужно настроить нашу среду выполнения.

Поскольку мы используем Джерси в качестве реализации JAX-RS, давайте добавим класс, который расширяет ResourceConfig и указывает пакет, который будет содержать классы, в которых мы определяем конечные точки REST:

@ApplicationPath("/linkrest")
public class LinkRestApplication extends ResourceConfig {

    public LinkRestApplication() {
        packages("com.baeldung.linkrest.apis");
        
        // load linkrest runtime
    }
}

В том же конструкторе , нам нужно создать и зарегистрировать LinkRestRuntime в контейнере Jersey. Этот класс основан на первой загрузке CayenneRuntime:

ServerRuntime cayenneRuntime = ServerRuntime.builder()
  .addConfig("cayenne-linkrest-project.xml")
  .build();
LinkRestRuntime lrRuntime = LinkRestBuilder.build(cayenneRuntime);
super.register(lrRuntime);

Наконец, нам нужно добавить класс в файл web.xml:

<servlet>
    <servlet-name>linkrest</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.baeldung.LinkRestApplication</param-value>
        </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>linkrest</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

5. Ресурсы REST

Теперь, когда у нас есть классов модели, мы можем начать писать ресурсы REST.

Конечные точки REST создаются с использованием стандартных аннотаций JAX-RS, а ответ строится с использованием класса LinkRest.

Наш пример будет состоять из написания серии конечных точек CRUD, которые обращаются к URL-адресу /department с использованием различных методов HTTP.

Во-первых, давайте создадим класс DepartmentResource, который сопоставляется с /department:

@Path("department")
@Produces(MediaType.APPLICATION_JSON)
public class DepartmentResource {

    @Context
    private Configuration config;
    
    // ...
}

Классу LinkRest нужен экземпляр класса конфигурации JAX-RS, который внедряется с помощью аннотации Context, также предоставляемой JAX. -РС.

Далее давайте продолжим писать каждую из конечных точек, которые обращаются к объектам отдела.

5.1. Создание объектов с помощью POST

Для создания объекта класс LinkRest предоставляет метод create(), который возвращает объект UpdateBuilder:

@POST
public SimpleResponse create(String data) {
    return LinkRest.create(Department.class, config).sync(data);
}

Параметр данных может быть либо одним объектом JSON, представляющим отдел, либо массивом объекты. Этот параметр отправляется в UpdateBuilder с помощью метода sync() для создания одного или нескольких объектов и вставки записей в базу данных, после чего метод возвращает SimpleResponse.

Библиотека определяет 3 дополнительных формата ответов:

    DataResponse\u003cT\u003e — ответ, представляющий набор T MetadataResponse\u003cT\u003e — содержит метаданные о типе SimpleResponse — объект, который содержит два атрибута успеха и сообщения

Далее, давайте используем curl для добавления записи отдела в базу данных:

curl -i -X POST -H "Content-Type:application/json" 
  -d "{"name":"IT"}" http://localhost:8080/linkrest/department

В результате команда возвращает статус 201 Created и атрибут успеха:

{"success":true}

~~ ~ Мы также можем создать несколько объектов, отправив массив JSON:

curl -i -X POST -H "Content-Type:application/json" 
  -d "[{"name":"HR"},{"name":"Marketing"}]" 
  http://localhost:8080/linkrest/department

5.2. Чтение сущностей с помощью GET

«Основным методом запроса объектов является метод select() класса LinkRest. Это возвращает объект SelectBuilder, который мы можем использовать для цепочки дополнительных методов запроса или фильтрации.

Давайте создадим конечную точку в классе DepartmentResource, которая возвращает все объекты отдела в базе данных:

@GET
public DataResponse<Department> getAll(@Context UriInfo uriInfo) {
    return LinkRest.select(Department.class, config).uri(uriInfo).get();
}

Вызов uri() задает информацию о запросе для SelectBuilder, а get() возвращает коллекцию упакованных отделов. как объект DataResponse\u003cDepartment\u003e.

Давайте посмотрим на отделы, которые мы добавили перед использованием этой конечной точки:

curl -i -X GET http://localhost:8080/linkrest/department

Ответ принимает форму объекта JSON с массивом данных и общим свойством:

{"data":[
  {"id":200,"name":"IT"},
  {"id":201,"name":"Marketing"},
  {"id":202,"name":"HR"}
], 
"total":3}

В качестве альтернативы получить набор объектов, мы также можем получить один объект, используя getOne() вместо get().

Давайте добавим конечную точку, сопоставленную с /department/{departmentId}, которая возвращает объект с заданным идентификатором. Для этого мы отфильтруем записи с помощью метода byId():

@GET
@Path("{id}")
public DataResponse<Department> getOne(@PathParam("id") int id, 
  @Context UriInfo uriInfo) {
    return LinkRest.select(Department.class, config)
      .byId(id).uri(uriInfo).getOne();
}

Затем мы можем отправить запрос GET на этот URL:

curl -i -X GET http://localhost:8080/linkrest/department/200

Результатом является массив данных с одним элементом :

{"data":[{"id":200,"name":"IT"}],"total":1}

5.3. Обновление объектов с помощью PUT

Для обновления записей мы можем использовать метод update() или createOrUpdate(). Последний будет обновлять записи, если они существуют, или создавать их, если они не существуют:

@PUT
public SimpleResponse createOrUpdate(String data) {
    return LinkRest.createOrUpdate(Department.class, config).sync(data);
}

Как и в предыдущих разделах, аргумент данных может быть одиночным объектом или массивом объектов.

Давайте обновим один из ранее добавленных отделов:

curl -i -X PUT -H "Content-Type:application/json" 
  -d "{"id":202,"name":"Human Resources"}" 
  http://localhost:8080/linkrest/department

Это возвращает объект JSON с сообщением об успехе или ошибке. После этого мы можем проверить, было ли изменено имя отдела с ID 202:

curl -i -X GET http://localhost:8080/linkrest/department/202

Действительно, эта команда возвращает объект с новым именем:

{"data":[
  {"id":202,"name":"Human Resources"}
],
"total":1}

5.4. Удаление объектов с помощью DELETE

И, чтобы удалить объект, мы можем вызвать метод delete(), который создает DeleteBuilder, а затем указать первичный ключ объекта, который мы хотим удалить, с помощью метода id():

@DELETE
@Path("{id}")
public SimpleResponse delete(@PathParam("id") int id) {
    return LinkRest.delete(Department.class, config).id(id).delete();
}

Затем мы можем вызвать эту конечную точку с помощью curl:

curl -i -X DELETE http://localhost:8080/linkrest/department/202

5.5. Работа с отношениями между объектами

LinkRest также содержит методы, упрощающие работу с отношениями между объектами.

Поскольку у отдела отношение «один ко многим» к сотруднику, давайте добавим конечную точку /department/{departmentId}/employees, которая обращается к классу EmployeeSubResource:

@Path("{id}/employees")
public EmployeeSubResource getEmployees(
  @PathParam("id") int id, @Context UriInfo uriInfo) {
    return new EmployeeSubResource(id);
}

Класс EmployeeSubResource соответствует отделу, поэтому он У вас будет конструктор, который задает идентификатор отдела, а также экземпляр конфигурации:

@Produces(MediaType.APPLICATION_JSON)
public class EmployeeSubResource {
    private Configuration config;

    private int departmentId;

    public EmployeeSubResource(int departmentId, Configuration configuration) {
        this.departmentId = departmentId;
        this.config = config;
    }

    public EmployeeSubResource() {
    }
}

Обратите внимание, что конструктор по умолчанию необходим для сериализации объекта как объекта JSON.

Далее давайте определим конечную точку, которая извлекает всех сотрудников из отдела:

@GET
public DataResponse<Employee> getAll(@Context UriInfo uriInfo) {
    return LinkRest.select(Employee.class, config)
      .toManyParent(Department.class, departmentId, Department.EMPLOYEES)
      .uri(uriInfo).get();
}

В этом примере мы использовали метод toManyParent() объекта SelectBuilder для запроса только объектов с заданным родителем.

Конечные точки для методов POST, PUT, DELETE могут быть созданы аналогичным образом.

Чтобы добавить сотрудников в отдел, мы можем вызвать конечную точку Departments/{departmentId}/employees с помощью метода POST:

curl -i -X POST -H "Content-Type:application/json" 
  -d "{"name":"John"}" http://localhost:8080/linkrest/department/200/employees

Затем давайте отправим запрос GET для просмотра сотрудников отдела:

curl -i -X GET "http://localhost:8080/linkrest/department/200/employees

Это возвращает объект JSON с массивом данных::

{"data":[{"id":200,"name":"John"}],"total":1}

6. Настройка ответа с помощью параметров запроса

LinkRest предоставляет простой способ настроить ответ, добавив в запрос определенные параметры. Их можно использовать для фильтрации, сортировки, разбиения на страницы или ограничения набора атрибутов набора результатов.

6.1. Фильтрация

Мы можем фильтровать результаты на основе значений атрибутов, используя параметр cayenneExp. Как следует из названия, это соответствует формату выражений Cayenne.

Давайте отправим запрос, который возвращает только отделы с именем «IT»:

curl -i -X GET http://localhost:8080/linkrest/department?cayenneExp=name='IT'

6.2. Сортировка

Для сортировки набора результатов необходимо добавить параметры sort и dir. Первый из них указывает атрибут для сортировки, а второй — направление сортировки.

Давайте посмотрим все отделы, отсортированные по имени:

curl -i -X GET "http://localhost:8080/linkrest/department?sort=name&dir=ASC"

6.3. Разбивка на страницы

Библиотека поддерживает разбивку на страницы, добавляя параметры start и limit:

curl -i -X GET "http://localhost:8080/linkrest/department?start=0&limit=2

«

«6.4. Выбор атрибутов

Используя параметры включения и исключения, мы можем контролировать, какие атрибуты или отношения возвращаются в результате.

curl -i -X GET "http://localhost:8080/linkrest/department?include=name

Например, давайте отправим запрос, который отображает только имена отделов:

curl -i -X GET "http://localhost:8080/linkrest/department?include=name&include=employees.name

Чтобы отобразить имена, а также сотрудников отдела только с их именем, мы можем использовать атрибут include дважды: ~ ~~

7. Заключение

В этой статье мы показали, как можно быстро предоставить модель данных через конечные точки REST с помощью платформы LinkRest.