Как сделать Spring Boot post API Idempotent

Я создал простой CRUD API, используя Spring Data JPA в моем загрузочном приложении Spring. Метод My Post в контроллере выглядит следующим образом:

@RequestMapping(value = "/article", method = RequestMethod.POST, produces = "application/json")
public Article createArticle(@RequestBody Article article) {
    return service.createArticle(article);
}

Метод обслуживания выглядит следующим образом: -

@Override
public Article createArticle(Article articleModel) {
    return repository.save(articleModel);
}

Мой JsonPayload выглядит следующим образом:

{
   "article_nm":"A1",
   "article_identifier":"unique identifier"
}

Теперь я хочу сделать свой запрос POST как Idempotent, чтобы даже если я снова получил полезную нагрузку json с тем же article_identifier, он не создал бы запись в БД.

Я не могу изменить схему/ограничение в базе данных, а поле article_identifier также не является первичным ключом в таблице.

Я понимаю, что сначала я могу проверить базу данных и вернуть сохраненную запись в ответ, если она уже существует, но здесь, если одновременно поступает несколько запросов (исходный и дублирующий запрос), оба будут проверять базу данных и не найдут записи с этим идентификатором и создаст 2 записи (по одной на каждую). Кроме того, поскольку это распределенное приложение, как я могу поддерживать согласованность между несколькими транзакциями базы данных.

Как я могу использовать какой-то механизм блокировки, чтобы никогда не было 2 записей с одинаковыми article_identifier. Может кто-нибудь предложить несколько ссылок, как реализовать это в Spring boot?


person Manish    schedule 20.07.2020    source источник
comment
Является ли article_identifier первичным ключом в базе данных?   -  person Kavithakaran Kanapathippillai    schedule 20.07.2020
comment
@kavithakaran Нет, это не так. Я уже упоминал об этом в своем посте.   -  person Manish    schedule 21.07.2020
comment
Не связано с проблемой, но обратите внимание, что в архитектуре REST GET, PUT и DELETE должны быть идемпотентными. Выполнение этого с POST может запутать потребителя API.   -  person Aleksandr Erokhin    schedule 24.12.2020


Ответы (1)


Идемпотентность в этом случае необходима для решения обратной отправки (или двойного почтового запроса). Простым способом было бы просто проверить на уровне обслуживания, существует ли сообщение с данной информацией (как вы указали). Для этого вы можете использовать repository.exists() вариантов.

Я понимаю, что сначала я могу проверить базу данных и вернуть сохраненную запись в ответ, если она уже существует

Что касается

если несколько запросов (оригинальный и дублирующий запрос) приходят одновременно, оба проверят базу данных и не найдут записи с этим идентификатором и создадут 2 записи (по одной для каждого)

Вам нужно изолировать транзакции друг от друга, если это одна база данных (я знаю, что вы сказали, что это не так, но я пытаюсь объяснить свои рассуждения, так что терпите меня). Для этой пружины есть следующая аннотация: @Transactional(isolation = Isolation.SERIALIZABLE). Хотя в этом случае @Transactional(isolation = Isolation.REPEATABLE_READ) было бы достаточно.

Кроме того, поскольку это распределенное приложение, как я могу поддерживать согласованность между несколькими транзакциями базы данных.

Как он распределяется? Сначала нужно подумать о базе данных. Это master-slave mysql/postgress/mongodb? Это какая-то странная глобально распределенная система? Предполагая, что это традиционная установка master-slave, тогда транзакция записи будет обрабатываться мастером (насколько мне известно, все выборки, принадлежащие транзакции, также будут там), поэтому проблем быть не должно. Однако ответ может быть действительно дан только в том случае, если будет предоставлено больше деталей.

person Mindaugas Bernatavičius    schedule 23.12.2020