Статьи
Утилиты Telegram YouTube Отзывы

Spring Data JPA, REST и Kotlin: создание, обновление, удаление

Видеогайд Исходники

16 марта 2023

Тэги: json, Kotlin, PostgreSQL, rest, Spring Boot, Spring Data, SQL, YouTube, руководство.

Содержание

  1. Data Transfer Object
  2. Создание записи
  3. Обновление записи
  4. Удаление записи

В предыдущей статье Spring Data JPA, REST и Kotlin: поиск записей мы научились искать записи по id и по части названия страны. Теперь рассмотрим, как реализовать создание записи, её обновление и удаление. Нам потребуется добавить в наше REST API три новых метода.

Data Transfer Object

Методы создания и обновления будут принимать на вход ранее созданную нами DTO (data transfer object), в которой будут указаны все параметры страны. Однако при создании id страны нам неизвестно, поэтому нужно модифицировать DTO так, чтобы оно допускало null в качестве id:

data class CountryDto(
    val id: Int? = null,
    val name: String,
    val population: Int,
)

После этого перейдём в сервисный слой и добавим 3 новых метода в интерфейс CountryService:

fun create(dto: CountryDto): Int

fun update(id: Int, dto: CountryDto)

fun delete(id: Int)

Метод создания принимает на вход только dto и возвращает id новой записи, т.к. это id устанавливается на стороне базы данных. Метод обновления принимает и dto, и отдельно id страны. Наконец, методу удаления само dto не требуется. Ему нужно указать лишь id страны.

Создание записи

Теперь реализуем метод удаления в CountryServiceImpl:

@Transactional
override fun create(dto: CountryDto): Int {
    val countryEntity = countryRepository.save(dto.toEntity())
    return countryEntity.id
}

Аннотация @Transactional указывает на то, что все операции внутри метода будут выполняться в транзакции. Её следует использовать всегда, когда у вас несколько запросов к БД в одном методе. Далее мы преобразуем полученную DTO в сущность с помощью вспомогательного метода расширения CountryDto.toEntity(). После чего передаём объект в метод save(), который появился в нашем репозитории благодаря наследованию от стандартного CrudRepository.

В качестве результата метод save() возвращает экземпляр сущности, в котором уже будет проставлен актуальный id новой записи, назначенный базой.

Вспомогательный метод расширения, преобразующий dto в сущность, выглядит так:

private fun CountryDto.toEntity(): CountryEntity =
    CountryEntity(
        id = 0,
        name = this.name,
        population = this.population,
    )

Поскольку id нам неизвестно, но сущность не допускает null, поэтому в id указываем 0. Spring Data JPA таким образом поймёт, что это новая сущность.

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

@PostMapping
fun create(@RequestBody dto: CountryDto): Int =
    countryService.create(dto)

Аннотация @PostMapping указывает на то, что данный запрос имеет тип POST. @RequestBody говорит о том, что dto будет передаваться в самом теле запроса в формате json.

Теперь мы можем запустить наш сервис и выполнить POST-запрос на создание новой записи:

curl -X POST http://127.0.0.1:8080/countries \
-H 'Content-Type: application/json' \
-d '{"name":"Япония","population":"123456"}'

Данная консольная команда должна быть записана в одну строку. Если вы разбиваете команду на несколько строк, то в конце каждой не забудьте добавить обратный слеш.

На практике для запросов изменения данных часто используют Postman.

POST-запрос на создание в Postman

В ответ нам приходит id новой записи.

Обновление записи

Перейдём к реализации метода обновления update().

@Transactional
override fun update(id: Int, dto: CountryDto) {
    var existingCountry = countryRepository.findByIdOrNull(id)
        ?: throw RuntimeException("Country not found")

    existingCountry.name = dto.name
    existingCountry.population = dto.population
    countryRepository.save(existingCountry)
}

Он также выполняется в транзакции. На вход поступает id существующей записи и новые значения полей в dto. Сначала мы подгружаем по id существующую запись с помощью стандартного метода findByIdOrNull(). Если запись не найдена, он вернёт null, и тогда мы кидаем исключение.

Затем в найденной записи меняем значения полей на новые, которые пришли к нам в dto. После чего вызываем тот же метод save(), что и при создании.

Обратите внимание, что мы не меняем id у существующей записи. Тогда Spring Data поймёт, что нужно обновить существующую запись. В ответ метод ничего не возвращает.

Добавим обработчик PUT-метода в CountryController:

@PutMapping("/{id}")
fun update(@PathVariable id: Int, @RequestBody dto: CountryDto) {
    countryService.update(id, dto)
}

@PathVariable связывает параметр метода id с частью урла, на который выполняется запрос. А новые значения полей самой сущности, как и в случае с созданием, передаются в dto.

PUT-запрос на обновление в Postman

Как видим, при обновлении в Postman нам ничего не приходит в ответ – значит, запрос выполнен успешно.

Удаление записи

Добавим реализацию метода удаления в сервисный слой. Он также выполняется в транзакции.

@Transactional
override fun delete(id: Int) {
    val existingCountry = countryRepository.findByIdOrNull(id)
        ?: throw RuntimeException("Country not found")
    countryRepository.deleteById(existingCountry.id)
}

В нём мы сначала пытаемся найти существующую запись. Как и в случае с обновлением, если запись не найдена, то кидаем исключение. Затем вызываем стандартный метод deleteById(), который появился в нашем репозитории благодаря наследованию от всё того же CrudRepository.

Наконец, добавим обработчик DELETE-метода в контроллер:

@DeleteMapping("/{id}")
fun delete(@PathVariable id: Int) {
    countryService.delete(id)
}

Как видите, у него единственный параметр id. Он является частью урла, на который выполняется запрос.

DELETE-запрос на удаление в Postman

В Postman в случае успешного удаления мы также ничего не возвращаем в ответе.

В следующей статье Spring Data JPA, REST и Kotlin: обработка ошибок мы научимся создавать собственные обработчики ошибок, а также изменим формат ответа при ошибке.



Комментарии

Добавить комментарий

×

devmark.ru