JPA: update only specific fields

I had the same question and as M. Deinum points out, the answer is no, you can’t use save. The main problem being that Spring Data wouldn’t know what to do with nulls. Is the null value not set or is it set because it needs to be deleted?

Now judging from you question, I assume you also had the same thought that I had, which was that save would allow me to avoid manually setting all the changed values.

So is it possible to avoid all the manuel mapping then? Well, if you choose to adhere to the convention that nulls always means ‘not set’ and you have the original model id, then yes.
You can avoid any mapping yourself by using Springs BeanUtils.

You could do the following:

  1. Read the existing object
  2. Use BeanUtils to copy values
  3. Save the object

Now, Spring’s BeanUtils actual doesn’t support not copying null values, so it will overwrite any values not set with null on the exiting model object. Luckily, there is a solution here:

How to ignore null values using springframework BeanUtils copyProperties?

So putting it all together you would end up with something like this

@RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<?> updateUser(@RequestBody User user) {

   User existing = userRepository.read(user.getId());
   copyNonNullProperties(user, existing);
   userRepository.save(existing);

   // ...
}

public static void copyNonNullProperties(Object src, Object target) {
    BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}

public static String[] getNullPropertyNames (Object source) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

    Set<String> emptyNames = new HashSet<String>();
    for(java.beans.PropertyDescriptor pd : pds) {
        Object srcValue = src.getPropertyValue(pd.getName());
        if (srcValue == null) emptyNames.add(pd.getName());
    }
    String[] result = new String[emptyNames.size()];
    return emptyNames.toArray(result);
}

Leave a Comment