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

Вернуться назад

24 апреля 2018

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

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

Начнём с доработки dao-слоя (интерфейс ProfileDao), где до сих пор был только один метод чтения данных.

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

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

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

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

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

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

    @Override
    public void insertProfile(String firstName, String lastName, int age) {
        MapSqlParameterSource 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. Здесь по сути только вызываем dao-метод:

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

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

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

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void createProfile(@Valid @RequestBody ProfileRequest request) {
        profileService.createProfile(
            request.getFirstName(),
            request.getLastName(),
            request.getAge()
        );
    }

В качестве аргумента метод принимает модель запроса ProfileRequest:

public class ProfileRequest {

    @NotNull
    private String firstName;

    @NotNull
    private String lastName;

    @NotNull
    @Min(1)
    private Integer age;

    // get- и set-методы...

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

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

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

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

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

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

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

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

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

Тэги: Java, SQL, Spring Boot, rest.


Исходники


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

Ваше имя:
Текст комментария: