Spring REST partial update with @PATCH method

后端 未结 4 1971
感动是毒
感动是毒 2021-01-31 12:47

I\'m trying to implement a partial update of the Manager entity based in the following:

Entity

public class Manager {
    private int id;
    private Str         


        
相关标签:
4条回答
  • 2021-01-31 12:58

    If you are truly using a PATCH, then you should use RequestMethod.PATCH, not RequestMethod.POST.

    Your patch mapping should contain the id with which you can retrieve the Manager object to be patched. Also, it should only include the fields with which you want to change. In your example you are sending the entire entity, so you can't discern the fields that are actually changing (does empty mean leave this field alone or actually change its value to empty).

    Perhaps an implementation as such is what you're after?

    @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);
    }
    
    0 讨论(0)
  • 2021-01-31 13:07

    First, you need to know if you are doing an insert or an update. Insert is straightforward. On update, use get() to retrieve the entity. Then update whatever fields. At the end of the transaction, Hibernate will flush the changes and commit.

    0 讨论(0)
  • 2021-01-31 13:10

    You can write custom update query which updates only particular fields:

    @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();
    }
    
    0 讨论(0)
  • 2021-01-31 13:19

    With this, you can patch your changes

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

    You will have to extend BeanUtilsBean to implement copying of non null values behaviour.

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

    and finally, mark NullAwareBeanUtilsBean as @Component

    or

    register NullAwareBeanUtilsBean as bean

    @Bean
    public NullAwareBeanUtilsBean nullAwareBeanUtilsBean() {
        return new NullAwareBeanUtilsBean();
    }
    
    0 讨论(0)
提交回复
热议问题