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

Добавление записи через POST-запрос в Spring Boot

Исходники

28 декабря 2023

Тэги: Java, json, maven, rest, Spring Boot, SQL, руководство.

Содержание

  1. Репозиторий
  2. Сервисный слой
  3. Контроллер

В статье Работа с БД в Spring Boot на примере postgresql мы узнали как читать данные из БД. Но чтение данных – это лишь малая часть всех операций, которые встречаются в типичном java-приложении. Теперь попробуем создать полноценный rest-интерфейс для добавления новых записей, их модификации и удаления.

За основу возьмём наше приложение из указанной статьи. Оно состоит из трёх слоёв: работа с БД (repository), бизнес-логика приложения (service) и сам rest-интерфейс (controller), который обрабатывает входящий json и генерирует исходящий.

Репозиторий

Начнём с доработки репозитория (интерфейс ProfileRepository), где до сих пор был только один метод чтения данных. Добавим метод вставки записи в БД:

void insertProfile(String firstName, String secondName, int age);

При добавлении новой записи нам достаточно всего три поля. id будет сгенерирован в БД автоматически.

В реализацию интерфейса ProfileRepositoryImpl добавляем sql-запросы в виде констант, которые принято размещать в начале класса:

private static final String SQL_INSERT_PROFILE =
        "insert into profile (first_name, last_name, age) values (:firstName, :lastName, :age)";

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

Вот реализация добавления записи в БД:

@Override
public void insertProfile(String firstName, String lastName, int age) {
    var params = new MapSqlParameterSource();
    params.addValue("firstName", firstName);
    params.addValue("lastName", lastName);
    params.addValue("age", age);
    jdbcTemplate.update(SQL_INSERT_PROFILE, params);
}

Всё предельно просто: сначала собираем параметры, затем передаём их вместе с запросом в метод jdbcTemplate.update(), который вопреки своему названию отвечает вообще за любые изменения данных (и insert, и update, и delete). Обратите внимание, что jdbcTemplate у нас имеет тип NamedParameterJdbcTemplate – именно он позволяет использовать именованные параметры. Иначе пришлось бы писать знаки вопроса.

Сервисный слой

Перейдём к сервисному слою. Интерфейс ProfileService не отличается оригинальностью:

void createProfile(String firstName, String secondName, int age);

Его реализация в ProfileServiceImpl. Здесь по сути только вызываем метод репозитория:

@Override
public void createProfile(String firstName, String secondName, int age) {
    profileRepository.insertProfile(firstName, secondName, age);
}

Контроллер

Согласно архитектуре restful-сервисов, чтение данных мы делаем при помощи GET-запросов, а создание – при помощи POST. Также хотелось бы отметить, что у GET-запроса не может быть тела запроса (body), все его параметры перечисляются в строке запроса. А POST-запрос может иметь тело, в которое мы будем помещать целевой json.

Сперва нам нужно добавить в проект ещё одну зависимость spring-boot-starter-validation, которая позволяет валидировать входящие запросы:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Перейдём к нашему контроллеру ProfileController и добавим в него метод создания профиля.

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void createProfile(@Valid @RequestBody ProfileRequest request) {
    profileService.createProfile(
            request.firstName(),
            request.lastName(),
            request.age()
    );
}

В качестве аргумента метод принимает объект ProfileRequest, реализованный как record-class:

public record ProfileRequest(
        @NotNull
        String firstName,
        @NotNull
        String lastName,
        @NotNull
        @Min(1)
        Integer age
) {
}

Эта модель представляет собой ровно тот объект, который мы ожидаем в теле запроса. Имена полей в json должны совпадать с именами полей в классе (при желании это можно переопределить дополнительной аннотацией). Дополнительно накладываем ограничения из пакета javax.validation.constraints на каждое поле.

Аннотация @NotNull применима для ссылочных типов и означает обязательность этого поля в запросе. Обратите внимание, что тут мы специально используем ссылочный Integer, который допускает null, а не примитивный int, который по умолчанию равен нулю. Ссылочный числовой тип вместе с @NotNull позволяет проверять обязательность числовых типов.

Аннотация @Min говорит сама за себя и ограничивает числовые типы снизу. Тем самым мы никогда не допустим, чтобы нам передали отрицательное число в качестве возраста.

Чтобы эти проверки работали для входящего json, параметр метода надо снабдить аннотацией @Valid, а чтобы именно в этот класс был преобразован входящий json, также добавляем @RequestBody. Внутри просто вызываем сервисный слой.

Аннотация @PostMapping указывает на то, что это обработчик POST-запроса. В скобках можно указать относительный урл, но в данном случае мы его не указали, поэтому запрос надо будет кидать по тому адресу, который указан на уровне контроллера, то есть /profiles. @ResponseStatus показывает, какой http-код нужно вернуть в ответ. В данном случае мы опять следуем rest-соглашениям и возвращаем код 201 – «Created».

Теперь мы готовы к тому, чтобы выполнить rest-запрос на создание новой записи в БД. Запускаем приложение и отравляем указанный POST-запрос по адресу http://127.0.0.1:8080/profiles. В http-заголовках обязательно указываем Content-Type: application/json.

{
  "firstName": "Петр",
  "lastName": "Сидоров",
  "age": 14
}

В ответ в случае успеха получаем http-статус 201. Если же мы попытаемся нарушить наши ограничения по полям, то получим json с подробным описанием ошибки.

Таким образом, Spring Boot позволяет буквально за 5 минут создать полноценный обработчик POST-запроса с валидацией входящих параметров.

В следующей статье Обновление записи через PUT-запрос в Spring Boot рассмотрим как можно обновлять записи.



Комментарии

29.10.2022 16:50 Антон

Подскажите, а как получить id сохраненного профиля, если мы возвращаем только статус CREATED?

29.10.2022 21:35 devmark

Касательно данного примера, полученный id можно посмотреть в БД после вставки. Если говорить о том, как получить этот id программно, то нужно в метод jdbcTemplate.update() передать дополнительный параметр. Этот параметр имеет тип GeneratedKeyHolder и после выполнения запроса в нём будет содержаться сгенерированный id. Также нужно в конце sql-запроса с insert дописать " returning id".

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

×

devmark.ru