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, H2, Linux, Hibernate, Collections, Stream API, многопоточность, чат-боты, нейросети, файлы, devops, Docker, Nginx, Apache, maven, gradle, JUnit, YouTube, новости, руководство, ООП, алгоритмы, головоломки, rest, GraphQL, Excel, XML, json, yaml.