Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring MVC PATCH method: partial updates

I have a project where I am using Spring MVC + Jackson to build a REST service. Let's say I have the following java entity

public class MyEntity {     private Integer id;     private boolean aBoolean;     private String aVeryBigString;     //getter & setters } 

Sometimes, I just want to update the boolean value, and I don't think that sending the whole object with its big string is a good idea just to update a simple boolean. So, I have considered using the PATCH HTTP method to only send the fields that need to be updated. So, I declare the following method in my controller:

@RequestMapping(method = RequestMethod.PATCH) public void patch(@RequestBody MyVariable myVariable) {     //calling a service to update the entity } 

The problem is: how do I know which fields need to be updated? For instance, if the client just wants to update the boolean, I will get an object with an empty "aVeryBigString". How am I supposed to know that the user just wants to update the boolean, but does not want to empty the string?

I have "solved" the problem by building custom URLs. For instance, the following URL: POST /myentities/1/aboolean/true will be mapped to a method that allows to only update the boolean. The problem with this solution is that it is not REST compliant. I don't want to be 100% REST compliant, but I do not feel comfortable with providing a custom URL to update each field (especially given that it causes problems when I want to update several fields).

Another solution would be to split "MyEntity" into multiple resources and just update these resources, but I feel like it does not make sense: "MyEntity" is a plain resource, it is not composed of other resources.

So, is there an elegant way of solving this problem?

like image 806
mael Avatar asked Jul 25 '13 14:07

mael


1 Answers

This could be very late, but for the sake of newbies and people who encounter the same problem, let me share you my own solution.

In my past projects, to make it simple, I just use the native java Map. It will capture all the new values including the null values that the client explicitly set to null. At this point, it will be easy to determine which java properties needs to be set as null, unlike when you use the same POJO as your domain model, you won't be able to distinguish which fields are set by the client to null and which are just not included in the update but by default will be null.

In addition, you have to require the http request to send the ID of the record you want to update, and do not include it on the patch data structure. What I did, is set the ID in the URL as path variable, and the patch data as a PATCH body.Then with the ID, you would get first the record via a domain model,then with the HashMap, you can just use a mapper service or utility to patch the changes to the concerned domain model.

Update

You can create a abstract superclass for your services with this kind of generic code, you must use Java Generics. This is just a segment of possible implementation, I hope you get the idea.Also it is better to use mapper framework such as Orika or Dozer.

public abstract class AbstractService<Entity extends BaseEntity, DTO extends BaseDto> {     @Autowired     private MapperService mapper;      @Autowired     private BaseRepo<Entity> repo;      private Class<DTO> dtoClass;      private Class<Entity> entityCLass;      public AbstractService(){        entityCLass = (Class<Entity>) SomeReflectionTool.getGenericParameter()[0];        dtoClass = (Class<DTO>) SomeReflectionTool.getGenericParameter()[1];     }      public DTO patch(Long id, Map<String, Object> patchValues) {         Entity entity = repo.get(id);         DTO dto = mapper.map(entity, dtoClass);         mapper.map(patchValues, dto);         Entity updatedEntity = toEntity(dto);         save(updatedEntity);         return dto;     } } 
like image 89
vine Avatar answered Sep 20 '22 23:09

vine