![](/img/trans.png)
[英]Patch REST API to Partial Update MongoDB in spring boot
[英]Spring REST partial update with @PATCH method
我正在嘗試基於以下內容實現 Manager 實體的部分更新:
實體
public class Manager {
private int id;
private String firstname;
private String lastname;
private String username;
private String password;
// getters and setters omitted
}
控制器中的 SaveManager 方法
@RequestMapping(value = "/save", method = RequestMethod.PATCH)
public @ResponseBody void saveManager(@RequestBody Manager manager){
managerService.saveManager(manager);
}
在 Dao impl 中保存對象管理器。
@Override
public void saveManager(Manager manager) {
sessionFactory.getCurrentSession().saveOrUpdate(manager);
}
當我保存對象時,用戶名和密碼已正確更改,但其他值為空。
所以我需要做的是更新用戶名和密碼並保留所有剩余數據。
如果您真的在使用 PATCH,那么您應該使用 RequestMethod.PATCH,而不是 RequestMethod.POST。
您的補丁映射應包含可用於檢索要修補的 Manager 對象的 ID。 此外,它應該只包含您想要更改的字段。 在您的示例中,您正在發送整個實體,因此您無法辨別實際發生變化的字段(空是否意味着不理會該字段或實際將其值更改為空)。
也許這樣的實現就是你所追求的?
@RequestMapping(value = "/manager/{id}", method = RequestMethod.PATCH)
public @ResponseBody void saveManager(@PathVariable Long id, @RequestBody Map<Object, Object> fields) {
Manager manager = someServiceToLoadManager(id);
// Map key is field name, v is value
fields.forEach((k, v) -> {
// use reflection to get field k on manager and set it to value v
Field field = ReflectionUtils.findField(Manager.class, k);
field.setAccessible(true);
ReflectionUtils.setField(field, manager, v);
});
managerService.saveManager(manager);
}
有了這個,您可以修補您的更改
1. Autowire `ObjectMapper` in controller;
2. @PatchMapping("/manager/{id}")
ResponseEntity<?> saveManager(@RequestBody Map<String, String> manager) {
Manager toBePatchedManager = objectMapper.convertValue(manager, Manager.class);
managerService.patch(toBePatchedManager);
}
3. Create new method `patch` in `ManagerService`
4. Autowire `NullAwareBeanUtilsBean` in `ManagerService`
5. public void patch(Manager toBePatched) {
Optional<Manager> optionalManager = managerRepository.findOne(toBePatched.getId());
if (optionalManager.isPresent()) {
Manager fromDb = optionalManager.get();
// bean utils will copy non null values from toBePatched to fromDb manager.
beanUtils.copyProperties(fromDb, toBePatched);
updateManager(fromDb);
}
}
您將不得不擴展BeanUtilsBean
以實現非空值行為的復制。
public class NullAwareBeanUtilsBean extends BeanUtilsBean {
@Override
public void copyProperty(Object dest, String name, Object value)
throws IllegalAccessException, InvocationTargetException {
if (value == null)
return;
super.copyProperty(dest, name, value);
}
}
最后,將 NullAwareBeanUtilsBean 標記為@Component
或
將NullAwareBeanUtilsBean
注冊為 bean
@Bean
public NullAwareBeanUtilsBean nullAwareBeanUtilsBean() {
return new NullAwareBeanUtilsBean();
}
首先,您需要知道您是在執行插入操作還是更新操作。 插入很簡單。 更新時,使用 get() 檢索實體。 然后更新任何字段。 在事務結束時,Hibernate 將刷新更改並提交。
您可以編寫僅更新特定字段的自定義更新查詢:
@Override
public void saveManager(Manager manager) {
Query query = sessionFactory.getCurrentSession().createQuery("update Manager set username = :username, password = :password where id = :id");
query.setParameter("username", manager.getUsername());
query.setParameter("password", manager.getPassword());
query.setParameter("id", manager.getId());
query.executeUpdate();
}
ObjectMapper.updateValue
提供了使用 dto 中的值部分映射實體所需的一切。 另外,您可以在此處使用以下兩個Map<String, Object> fields
: Map<String, Object> fields
或String json
,因此您的服務方法可能如下所示:
@Autowired
private ObjectMapper objectMapper;
@Override
@Transactional
public Foo save(long id, Map<String, Object> fields) throws JsonMappingException {
Foo foo = fooRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Foo not found for this id: " + id));
return objectMapper.updateValue(foo , fields);
}
作為對 Lane Maxwell 答案的第二個解決方案和補充,您可以使用Reflection
僅映射已發送值的 Map 中存在的屬性,因此您的服務方法可能如下所示:
@Override
@Transactional
public Foo save(long id, Map<String, Object> fields) {
Foo foo = fooRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Foo not found for this id: " + id));
fields.keySet()
.forEach(k -> {
Method method = ReflectionUtils.findMethod(LocationProduct.class, "set" + StringUtils.capitalize(k));
if (method != null) {
ReflectionUtils.invokeMethod(method, foo, fields.get(k));
}
});
return foo;
}
第二種解決方案允許您在映射過程中插入一些額外的業務邏輯,可能是轉換或計算等。
也不同於查找反射字段Field field = ReflectionUtils.findField(Foo.class, k);
通過名稱而不是使其可訪問,查找屬性的 setter 實際上調用 setter 方法,該方法可能包含要執行的附加邏輯並防止將值設置為私有屬性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.