Как все мы знаем, существует большая проблема с частичным обновлением сущности.Поскольку автоматическое преобразование строк json в сущность, все поля, которые не были перенесены, будут помечены как пустые.И в результате поля, которые мы не хотели сбрасывать, будут сброшены.
Я покажу классическую схему:
@RestController
@RequestMapping(EmployeeController.PATH)
public class EmployeeController {
public final static String PATH = "/employees";
@Autowired
private Service service;
@PatchMapping("/{id}")
public Employee update(@RequestBody Employee employee, @PathVariable Long id) {
return service.update(id, employee);
}
}
@Service
public class Service {
@Autowired
private EmployeeRepository repository;
@Override
public Employee update(Long id, Employee entity) {
Optional<T> optionalEntityFromDB = repository.findById(id);
return optionalEntityFromDB
.map(e -> saveAndReturnSavedEntity(entity, e))
.orElseThrow(RuntimeException::new);
}
private T saveAndReturnSavedEntity(Employee entity, Employee entityFromDB) {
entity.setId(entityFromDB.getId());
return repository.save(entity);
}
}
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
и, как я уже говорил, в текущемВ результате реализации мы не сможем выполнить частичное обновление каким-либо образом.То есть невозможно отправить обновление только одного поля в строке json;все поля будут обновлены и будут иметь значение NULL (исключение передано).
Решение этой проблемы заключается в том, что вам необходимо выполнить преобразование из строки json в сущность в ручном режиме.То есть не используйте всю магию Spring Boot (что очень печально).
Я также приведу пример того, как это можно реализовать с помощью слияния на уровне json:
@RestController
@RequestMapping(EmployeeRawJsonController.PATH)
public class EmployeeRawJsonController {
public final static String PATH = "/raw-json-employees";
@Autowired
private EmployeeRawJsonService service;
@PatchMapping("/{id}")
public Employee update(@RequestBody String json, @PathVariable Long id) {
return service.update(id, json);
}
}
@Service
public class EmployeeRawJsonService {
@Autowired
private EmployeeRepository employeeRepository;
public Employee update(Long id, String json) {
Optional<Employee> optionalEmployee = employeeRepository.findById(id);
return optionalEmployee
.map(e -> getUpdatedFromJson(e, json))
.orElseThrow(RuntimeException::new);
}
private Employee getUpdatedFromJson(Employee employee, String json) {
Long id = employee.getId();
updateFromJson(employee, json);
employee.setId(id);
return employeeRepository.save(employee);
}
private void updateFromJson(Employee employee, String json) {
try {
new ObjectMapper().readerForUpdating(employee).readValue(json);
} catch (IOException e) {
throw new RuntimeException("Cannot update from json", e);
}
}
}
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
С помощью этого решения мы устраняем проблему, связанную с частичным обновлением.
Но здесь возникает другая проблема: мы теряем автоматическое добавление проверки бинов.
То естьВ первом случае проверки достаточно, чтобы добавить одну аннотацию @Valid:
@PatchMapping("/{id}")
public Employee update(@RequestBody @Valid Employee employee, @PathVariable Long id) {
return service.update(id, employee);
}
Но мы не можем сделать то же самое, когда выполняем десериализацию вручную.
Мой вопрос: есть ли способ включить автоматическую проверку для второго случая?Или, может быть, есть другие решения, которые позволяют вам использовать магию Spring Boot для проверки бинов.