Java 使用@PATCH方法对弹簧座进行部分更新

Java 使用@PATCH方法对弹簧座进行部分更新,java,spring,rest,Java,Spring,Rest,我正在尝试基于以下内容对Manager实体进行部分更新: 实体 public class Manager { private int id; private String firstname; private String lastname; private String username; private String password; // getters and setters omitted } 控制器中的SaveManager方法

我正在尝试基于以下内容对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);
}
保存对象时,用户名和密码已正确更改,但其他值为空


因此,我需要做的是更新用户名和密码并保留所有剩余数据。

您可以编写自定义更新查询,只更新特定字段:

@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();
}

首先,您需要知道您是在执行插入还是更新。插入很简单。更新时,使用get()检索实体。然后更新所有字段。事务结束时,Hibernate将刷新更改并提交。

如果您确实在使用修补程序,则应使用RequestMethod.PATCH,而不是RequestMethod.POST

修补程序映射应包含可用于检索要修补的Manager对象的id。此外,它应该只包括要更改的字段。在您的示例中,您发送的是整个实体,因此无法识别实际正在更改的字段(empty是指不处理此字段还是实际将其值更改为空)

也许您所追求的就是这样一个实现

@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);
}
@RequestMapping(value=“/manager/{id}”,method=RequestMethod.PATCH)
public@ResponseBody void saveManager(@PathVariable Long id,@RequestBody映射字段){
Manager Manager=someServiceToLoadManager(id);
//映射键是字段名,v是值
字段。forEach((k,v)->{
//使用反射获取管理器上的字段k,并将其设置为值v
Field=ReflectionUtils.findField(Manager.class,k);
字段。setAccessible(true);
ReflectionUtils.setField(字段,管理器,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);
        }
    }
最后,将null标记为
@Component

NullAwareBeanUtilsBean
注册为bean

@Bean
public NullAwareBeanUtilsBean nullAwareBeanUtilsBean() {
    return new NullAwareBeanUtilsBean();
}

它工作得很好,但在这种情况下与使用POST和PATCH无关,对吗?这取决于您的需要。在我看来,最好使用PUT(或POST)进行修改,当RequestBody是纯文本形式时,该方法是什么?您是否使用转换器将纯文本转换为地图?@lane.maxwell Ya如果我们使用RequestMethod.PATCH,我完全同意您的回答,那么我们需要使用地图,但我有一些困惑,请您清除它好吗?,若我改变了方法类型补丁,将所有的更改保持原样,那个么它对数据库的作用是相同的,那个么它是如何不同的。我知道PUT和PATCH-1有两个区别。如果不存在,请创建新条目。2.仅更改特定值,而不是整个JSON。对于JPA来说,它有用吗?是的,那么怎么做呢?@DhwanilPatel从你的代码和它的行为的角度来看,PUT、PATCH甚至POST的行为都是一样的。为了遵守HTTP规范,PUT请求应该包含整个实体,而补丁将只包含您要修改的属性。感谢您的解决方案。但是当我没有在'findField'和'setField'之间编写命令:“field.setAccessible(true);”时,我遇到了InvocationTargetException。希望它能帮助一些人。这是一个很好的评论,因为setField假设字段是可访问的。我要修改答案“user”来自哪里,它是什么?@Boris My bad,它应该是
manager
param。更正了代码。那么,如果我们真的想将字段值更改为NULL呢!从BeanAwareNullUtilsGood Response中删除“value==null”检查。更简单的是,
BeanUtilsBean
的文档说明:如果源“bean”实际上是一个映射,则假定它包含字符串值的简单属性名作为键,指向将被转换(如有必要)并在目标bean中设置的相应属性值。也就是说,可以跳过Jackson部分和NullAwareBeanUtilsBean部分。只需使用
BeanUtilsBean.getInstance().copyProperties(fromDb,manager)。我在这篇文章中总结了一些关于如何使用
补丁的详细信息。这篇文章中描述的方法在GitHub上可用。
@Bean
public NullAwareBeanUtilsBean nullAwareBeanUtilsBean() {
    return new NullAwareBeanUtilsBean();
}