14 марта 2023
Тэги: gradle, Hibernate, Kotlin, PostgreSQL, rest, Spring Boot, Spring Data, SQL, yaml, YouTube, руководство.
В предыдущей статье Spring Data JPA, REST и Kotlin: заготовка проекта мы сделали заготовку проекта и научились отдавать статический список стран. А сейчас разберёмся с подключением к базе.
Теперь пришла пора подключиться к базе данных и научиться считывать данные из неё. Сначала создадим таблицу country и наполним её данными:
В результате выполнения этих запросов наша таблица будет выглядеть так:
Затем в проекте в папке resources переименуем файл application.properties в application.yml и пропишем там настройки подключения к БД (здесь вам нужно указать свои настройки):
driver-class-name – это класс драйвера вашей СУБД, он берётся как раз из зависимости postgres. url, username и password указываете свои собственные. В секции jpa.properties.hibernate можно указать некоторые дополнительные параметры. Например, show_sql позволяет отображать в логах sql-запросы, которые будет генерить Spring Data.
Теперь раскомментируем ранее закомментированные нами зависимости spring-boot-starter-data-jpa и org.postgresql:postgresql в файле build.gradle.kts.
Создадим сущность CountryEntity, поля которой будут мапиться на поля таблицы country в базе данных.
Аннотация @Entity указывает на то, что перед нами именно сущность, жизненный цикл которой управляется Spring Data. Аннотация @Table в явном виде позволяет задать название таблицы, с которой связана эта сущность. Если имя таблицы и имя сущности совпадают, данную аннотацию вообще можно опустить.
Обратите внимание, что мы создаём обычный class, а не data class. Это связано с внутренней реализацией Spring Data и неизменяемые data-классы нам здесь не подходят. По той же причине все поля, которые могут менять свои значения, помечаются как var.
Аннотация @Id указывает поле, которое определяет уникальность сущности. @GeneratedValue указывает на способ присвоения значений этому идентификатору. В данном случе у нас автоинкремент, реализуемый средствами СУБД.
Перейдём к слою репозитория и создадим интерфейс CountryRepository. Наследуем его от стандартного интерфейса CrudRepostitory, который типизируется нашей сущностью и типом Int (тип поля id в сущности).
И здесь начинается самое интересное, поскольку нам не нужно писать реализацию интерфейса. Spring Data сам сгенерит реализацию и sql-запросы под нашу конкретную СУБД автоматически!
Нам остаётся только внедрить этот интерфейс в сервисный слой и вызвать у него метод findAll() вместо нашего хардкода:
С помощью метода расширения CountryEntity.toDto() осуществляем преобразование списка сущностей базы данных в список dto, который отдаём как результат работы сервиса. На первый взгляд это может показаться избыточным, но с ростом количества полей у сущности часть из них может потребоваться скрыть от внешних пользователей. Или наоборот, добавить в dto какие-то дополнительные поля, вычисляемые динамически.
Теперь запустим наш проект и выполним тот же http-запрос, что делали ранее. В ответе мы увидим не хардкод, а именно те сущности, которые лежат в таблице country.
Если вы внимательно посмотрите на тот список, который пришёл нам в ответ, вы увидите, что его сортировка произвольна. Если же мы хотим задать явную сортировку, например, по названию, то нам достаточно определить в интерфейсе репозитория новый метод, следуя определённым соглашениям об именовании.
В данном случае его нужно назвать findByOrderByName(). Более подробно про соглашения об именовании читайте в статье Написание запросов в Spring Data JPA.
И в сервисном слое заменить вызов метода findAll().
Теперь снова запустим проект и убедимся, что страны отсортированы в алфавитном порядке.
Выводить все имеющиеся в таблице страны и сортировать их по алфавиту это, конечно, удобно. Но что делать, если стран будет слишком много? Можно реализовать постраничный вывод. И Spring Data позволяет это делать с минимальными трудозатратами.
В метод репозитория добавим параметр типа Pageable.
Очень важно, чтобы при постраничном выводе у вас была хоть какая-то сортировка. В противном случае для одной и той же страницы вы можете получать разные результаты.
В метод сервисного слоя добавим параметр pageIndex.
В репозиторий мы передаём объект PageRequest, который является реализацией интерфейса Pageable. Метод PageRequest.of() принимает два параметра: номер страницы (0 – первая страница) и количество элементов на странице.
В метод контроллера также добавляем параметр pageIndex:
Аннотация @RequestParam связывает его с параметром запроса page.
Теперь запускаем наш проект и, чтобы получить список стран на первой странице, выполняем следующий запрос:
Увеличивая параметр page мы постепенно можем пройтись по всем страницам. И на каждой будет не более двух стран.
В следующей статье Spring Data JPA, REST и Kotlin: поиск записей мы научимся искать страны по id и по названию.
Kotlin, Java, Spring, Spring Boot, Spring Data, SQL, PostgreSQL, Oracle, Linux, Hibernate, Collections, Stream API, многопоточность, файлы, Nginx, Apache, maven, gradle, JUnit, YouTube, новости, руководство, ООП, алгоритмы, головоломки, rest, GraphQL, Excel, XML, json, yaml.