我尝试根据以下内容实现Manager实体的部分更新:
实体
public class Manager {
private int id;
private String firstname;
private String lastname;
private String username;
private String password;
// getters and setters omitted
}
Controller中的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);
}
当我保存对象时,用户名和密码已正确更改,但其他值为空。
所以我需要做的是更新用户名和密码并保留所有剩余的数据。
答案 0 :(得分:11)
如果你真的使用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 k
Field field = ReflectionUtils.findField(Manager.class, k);
ReflectionUtils.setField(field, manager, v);
});
managerService.saveManager(manager);
}
答案 1 :(得分:8)
有了这个,您可以修补您的更改
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();
}
答案 2 :(得分:1)
首先,您需要知道您是在进行插入还是更新。插入很简单。在更新时,使用get()来检索实体。然后更新任何字段。在事务结束时,Hibernate将刷新更改并提交。
答案 3 :(得分:0)
您可以编写自定义更新查询,仅更新特定字段:
@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();
}
答案 4 :(得分:0)
ObjectMapper.updateValue
提供了使用 dto 中的值部分映射实体所需的一切。
另外,您可以在此处使用以下两种方法之一: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 方法,该方法可能包含要执行的附加逻辑并防止将值设置为私有属性。