Открывать репозитории Spring Data через REST довольно просто с Spring Boot и Spring Data REST. С минимальным кодом можно создавать REST-представления объектов JPA, которые следуют принципу HATEOAS . Я решил повторно использовать JPA-сущности Spring PetClinic (бизнес-уровень) в качестве основы для этой статьи.
Основа приложения
Модель PetClinic относительно проста, но она состоит из однонаправленных и двунаправленных ассоциаций, а также базового наследования:
Кроме того, Spring PetClinic предоставляет сценарии SQL для HSQLDB, что делает создание схемы и заполнение ее образцами данных в моем новом приложении очень простым.
Зависимости проекта
В качестве основы для конфигурации я использовал Spring Initializr и создал базовый проект Gradle. Чтобы использовать Spring Data REST в приложении Spring Boot, я добавил следующие загрузчики:
1
2
3
|
compile( "org.springframework.boot:spring-boot-starter-web" ) compile( "org.springframework.boot:spring-boot-starter-data-jpa" ) compile( "org.springframework.boot:spring-boot-starter-data-rest" ) |
Кроме того, я добавил зависимость HSQLDB в проект:
1
|
compile( "org.hsqldb:hsqldb:2.3.2" ) |
Исходный проект использует org.joda.time.DateTime
для полей даты и использует org.jadira.usertype.dateandtime.joda.PersistentDateTime
что позволяет сохранять его в Hibernate. Чтобы использовать его в новом проекте, мне нужно было добавить следующие зависимости:
1
2
|
compile( "joda-time:joda-time:2.4" ) compile( "org.jadira.usertype:usertype.jodatime:2.0.1" ) |
Работая с API, я заметил, что хотя поля date
в исходном проекте были аннотированы с помощью Spring @DateTimeFormat
они не были должным образом сериализованы. Я обнаружил, что мне нужно использовать @JsonFormatter
, поэтому в build.gradle
была добавлена еще одна зависимость:
1
|
compile( "com.fasterxml.jackson.datatype:jackson-datatype-joda:2.4.2" ); |
Оказавшись в пути к классам, Spring Boot автоматически настраивает com.fasterxml.jackson.datatype.joda.JodaModule
через org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
.
Обратите внимание, что если вы хотите правильно сериализовать типы даты и времени Java 8, вам нужно добавить в проект зависимость типа данных JSON310 от Jackson .
Инициализация базы данных
Для инициализации источника данных я добавил файлы schema-hsqldb.sql
и data-hsqldb.sql
в src/main/resources
. Наконец, я добавил несколько свойств в application.properties
:
1
2
3
|
spring.datasource.platform = hsqldb spring.jpa.generate-ddl = false spring.jpa.hibernate.ddl-auto = none |
Теперь при запуске приложения файлы будут автоматически выбраны, а источник данных будет инициализирован, и обнаружение API будет намного проще, поскольку есть данные!
Хранилища
Основная идея Spring Data REST заключается в том, что он построен на основе репозиториев Spring Data и автоматически экспортирует их как ресурсы REST . Я создал несколько репозиториев, по одному для каждой сущности ( OwnerRepository
, PetRepository
и т. Д.). Все репозитории являются интерфейсами Java, расширяющимися из PagingAndSortingRepository
.
На этом этапе не @Controller
никакого дополнительного кода: нет @Controller
s, нет конфигурации (если не требуется настройка). Spring Boot автоматически настроит все для нас.
Запуск приложения
При наличии всей конфигурации проект может быть выполнен (вы найдете ссылку на весь проект внизу статьи). Если вам повезет, приложение запустится, и вы сможете перейти по http://localhost:8080
который указывает на коллекцию ссылок на все доступные ресурсы ( корневой ресурс ). Тип содержимого ответа:
HAL
Ресурсы реализованы в стиле Hypermedia, и по умолчанию Spring Data REST использует HAL с типом контента application/hal+json
для визуализации ответов. HAL — это простой формат, который позволяет легко связывать ресурсы. Пример:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
$ curl localhost: 8080 /owners/ 1 { "firstName" : "George" , "lastName" : "Franklin" , "_links" : { "self" : { }, "pets" : { } } } |
С точки зрения Spring Data REST, существует несколько типов ресурсов: коллекция, элемент, поиск, метод запроса и связь, и все они используют тип содержимого application/hal+json
в ответах.
Коллекционный и предметный ресурс
Ресурсы коллекции поддерживают как методы GET
и POST
. Ресурсы элементов обычно поддерживают методы GET
, PUT
, PATCH
и DELETE
. Обратите внимание, что PATCH
применяет значения, отправленные с телом запроса, тогда как PUT
заменяет ресурс.
Поиск и поиск метода ресурса
Ресурс поиска возвращает ссылки для всех методов запроса, предоставляемых репозиторием, тогда как ресурс метода запроса выполняет запрос, предоставляемый через отдельный метод запроса в интерфейсе репозитория. Оба доступны только для чтения, поэтому поддерживают только метод GET
.
Чтобы визуализировать это, я добавил метод find в OwnerRepository
:
1
|
List<Owner> findBylastName( @Param ( "lastName" ) String lastName); |
Который затем был выставлен в http://localhost:8080/owners/search
:
1
2
3
4
5
6
7
8
9
|
$ curl http: //localhost:8080/owners/search { "_links" : { "findBylastName" : { "templated" : true } } } |
Ресурс ассоциации
Spring Data REST предоставляет субресурсы автоматически. Ресурс ассоциации поддерживает методы GET
, POST
и PUT
.
и позволить управлять ими. При работе с ассоциацией вы должны знать тип содержимого text / uri-list . Запросы с этим типом содержимого содержат один или несколько URI ( каждый URI должен отображаться в одной и только одной строке ) ресурса для добавления в ассоциацию.
В первом примере мы рассмотрим однонаправленное отношение в классе Vet
:
1
2
3
4
|
@ManyToMany (fetch = FetchType.EAGER) @JoinTable (name = "vet_specialties" , joinColumns = @JoinColumn (name = "vet_id" ), inverseJoinColumns = @JoinColumn (name = "specialty_id" )) private Set<Specialty> specialties; |
Чтобы добавить существующие специальности в коллекцию специальностей ветеринара, необходимо выполнить запрос PUT
:
1
|
curl -i -X PUT -H "Content-Type:text/uri-list" -d $ 'http://localhost:8080/specialties/1\nhttp://localhost:8080/specialties/2' http://localhost: 8080 /vets/ 1 /specialties |
Удаление ассоциации может быть сделано с помощью метода DELETE
как DELETE
ниже:
1
|
curl -i -X DELETE http: //localhost:8080/vets/1/specialties/2 |
Давайте посмотрим на другой пример:
1
2
3
4
5
6
7
8
|
// Owner @OneToMany (mappedBy = "owner" , cascade = CascadeType.ALL, orphanRemoval = true ) private Set<Pet> pets; // Pet @ManyToOne (cascade = CascadeType.ALL, optional = false ) @JoinColumn (name = "owner_id" ) private Owner owner; |
Установка владельца домашнего животного может быть сделана с помощью запроса ниже:
1
|
curl -i -X PUT -H "Content-Type:text/uri-list" -d "http://localhost:8080/owners/1" http://localhost: 8080 /pets/ 2 /owner |
Но как насчет удаления владельца? Так как владелец должен быть всегда установлен для питомца, мы получаем HTTP/1.1 409 Conflict
, пытаясь сбросить его с помощью следующей команды:
1
|
curl -i -X DELETE http: //localhost:8080/pets/2/owner |
Интеграционные тесты
Spring Boot позволяет запускать веб-приложение в тесте и проверять его с помощью @IntegrationTest
Spring Boot. Вместо использования MockMvc
контекста веб-приложения на стороне сервера ( MockMvc
) мы будем использовать RestTemplate
и его реализацию Spring Boot для проверки реальных вызовов REST.
Как мы уже знаем, ресурсы относятся к типу контента application/hal+json
. Таким образом, фактически невозможно будет десериализовать их непосредственно в объект сущности (например, Owner
). Вместо этого он должен быть десериализован в org.springframework.hateoas.Resource
который оборачивает сущность и добавляет к ней ссылки. А поскольку Resource
является универсальным типом, необходимо использовать RestTemplate
с RestTemplate
.
В приведенном ниже примере показано, что:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
private RestTemplate restTemplate = new TestRestTemplate(); @Test public void getsOwner() { ParameterizedTypeReference<Resource<Owner>> responseType = new ParameterizedTypeReference<Resource<Owner>>() {}; ResponseEntity<Resource<Owner>> responseEntity = restTemplate.exchange(ownerUrl, GET, null , responseType); Owner owner = responseEntity.getBody().getContent(); assertEquals( "George" , owner.getFirstName()); // more assertions } |
Этот подход хорошо описан в следующей статье: Использование службы отдыха Spring-hateoas с использованием токенов типа Spring RestTemplate и Super
Резюме
С помощью нескольких шагов и возможностей Spring Boot и Spring Data REST я создал API для существующей базы данных PetClinic. С Spring Data REST можно сделать гораздо больше (например, настройку), и, помимо довольно скудной документации, по сравнению с другими проектами Spring, кажется, что Spring Data REST может значительно ускорить разработку. На мой взгляд, это хороший проект, когда нужно быстро создавать прототипы.
использованная литература
- Исходный код
- Spring Boot PetClinic API на GitHub
- Документация:
- Статьи:
Ссылка: | Spring Boot и Spring Data REST — демонстрация репозиториев поверх REST от нашего партнера JCG Рафаля Боровца в блоге Codeleak.pl . |