Spring JPA — частичное обновление RESTful и проверка для объекта

#java #spring #spring-boot #spring-data-jpa

#java #spring #spring-boot #spring-data-jpa

Вопрос:

У меня есть простой RESTful API, основанный на Spring MVC, использующий подключенную к JPA базу данных MySQL. До сих пор этот API поддерживает только полные обновления объекта. Это означает, что все поля должны быть предоставлены внутри тела запроса.

 @ResponseBody
@PutMapping(value = "{id}")
public ResponseEntity<?> update(@Valid @RequestBody Article newArticle, @PathVariable("id") long id) {

    return service.updateById(id, newArticle);
}
  

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

 @Entity
public class Article {

    @NotEmpty @Size(max = 100) String title;
    @NotEmpty @Size(max = 500) String content;

    // Getters and Setters
}
  

Пример для тела запроса частичного обновления {"content": "Just a test"} вместо {"title": "Title", "content": "Just a test"} .
Фактическое частичное обновление выполняется путем проверки, не является ли данное поле null:

 if(newArticle.getTitle() != null) article.setTitle(newArticle.getTitle());
  

Но проверка, конечно, не сработает! Я должен отключить проверку для метода обновления, чтобы запустить службу RESTful. У меня, по сути, два вопроса:

  • Как я могу проверить только «существующее» подмножество свойств в методе обновления, по-прежнему требуя всех полей при создании?
  • Есть ли более элегантный способ частичного обновления, чем проверка на null?

Комментарии:

1. Когда вы комментируете поле «title» с помощью @NotEmpty , это означает, что у вас не может быть экземпляра Article с нулевым заголовком.

2. @Hua Конечно, да, это намеренно, потому что не должно быть возможности создать статью без заголовка… Просто путем проверки тела запроса на обновление каждое поле должно быть необязательным, но не во время создания.

3. @0x1C1B пожалуйста, рассмотрите возможность принятия другого ответа, он более адекватен

Ответ №1:

Сложность частичных обновлений и Spring JPA заключается в том, что вы можете отправить половину заполненных полей, и даже то, что вам нужно будет извлечь весь объект из базы данных, а затем просто «объединить» как объект, так и pojo, потому что в противном случае вы рискуете своими данными, отправляя значения null в базу данных.

Но само объединение довольно сложно, потому что вам нужно работать с каждым полем и принимать решение либо отправить новое значение в базу данных, либо просто сохранить текущее. И по мере добавления полей проверка должна обновляться, а тесты становятся более сложными. В одном единственном заявлении: он не масштабируется. Идея состоит в том, чтобы всегда писать код, который открыт для расширения и закрыт для модификаций. Если вы добавите больше полей, то блок проверки в идеале не нужно менять.

Способ, которым вы справляетесь с этим в модели REST, заключается в том, что вы работаете со всем объектом каждый раз, когда вам нужно. Допустим, у вас есть пользователи, затем вы сначала извлекаете пользователя:

 GET /user/100
  

Тогда у вас на вашей веб-странице будут все поля с идентификатором пользователя = 100. Затем вы меняете его фамилию. Вы распространяете изменение, вызывая тот же URL ресурса с помощью глагола PUT:

 PUT /user/100
  

И вы отправляете все поля или, скорее, «тот же объект» обратно с новой фамилией. И вы забываете о проверке, проверка будет просто работать как черный ящик. Если вы добавляете больше полей, вы добавляете больше @NotNull или любой другой проверки, которая вам нужна. Конечно, могут быть ситуации, когда вам действительно нужно писать блоки кода для проверки. Даже в этом случае проверка не будет затронута, так как у вас будет основной цикл for для вашей проверки, и у каждого поля будет свой собственный валидатор. Если вы добавляете поля, вы добавляете средства проверки, но основной блок проверки остается неприкосновенным.

Комментарии:

1. Очень хороший подход! И как бы вы порекомендовали обращаться с неизменяемыми полями, такими как имя пользователя или идентификатор? Должен ли я явно игнорировать эти поля? Чем я также должен объединить их правильно?

2. Что ж, использование аннотаций проверки Java позволило бы вам выполнять некоторые базовые вещи, такие как проверки пустой, нулевой длины. Если определенное поле не может быть обновлено, например, имя пользователя, тогда вам нужно будет написать логику. Это те случаи, которые определяются вашими внутренними бизнес-правилами или правилами организации. На первом уровне у вас будут все проверки @Valid. Затем ваш код начнет выполняться. В вашем уровне DAO может быть логика для извлечения текущего имени пользователя из сущности и сравнения его с тем, которое поступает в полезной нагрузке. Это всего лишь пример, есть много способов.

3. Моей настоящей целью было показать вам, как работать в средах REST, когда вы извлекаете и перемещаете целые объекты для простоты и надежности.