0

I have a very simple task, I have a "User" Entity.

This user has tons of fields, for example :

firstName age country .....

My goal is to expose a simple controller for update:

@RequestMapping(value = "/mywebapp/updateUser")
public void updateUser(data)

I would like clients to call my controller with updates that might include one or more fields to be updated.

What are the best practices to implement such method?

One naive solution will be to send from the client the whole entity, and in the server just override all fields, but that seems very inefficient.

another naive and bad solution might be the following:

 @Transactional
 @RequestMapping(value = "/mywebapp/updateUser")
 public void updateUser(int userId, String[] fieldNames, String[] values) {

   User user = this.userDao.findById(userId);

    for (int i=0 ; i < fieldsNames.length ; i++) {

        String fieldName = fieldsName[i];

        switch(fieldName) {
          case fieldName.equals("age") {

           user.setAge(values[i]);
          }
          case fieldName.equals("firstName") {

           user.setFirstName(values[i]);
          }
          ....
        } 
    }
}

Obviously these solutions aren't serious, there must be a more robust\generic way of doing that (reflection maybe).

Any ideas?

1
  • Most of the approaches that were considered by my team revolved around something like this itself. An option could be to consider non null entries as 'updated' but that would break the logic where the intention is to set a null. The other option is to have a metadata for each property (User in this case). The property in the metadata being a isModified/dirty flag and then it boils down to your solution. Commented Aug 17, 2014 at 10:16

2 Answers 2

1

I once did this genetically using Jackson. It has a very convenient ObjectMapper.readerForUpdating(Object) method that can read values from a JsonNode/Tree onto an existing object.

The controller/service

@PATCH
@Transactional
public DomainObject partialUpdate (Long id, JsonNode data) {
  DomainObject o = repository.get(id);
  return objectMapper.readerForUpdating(o).readValue(data);
}

That was it. We used Jersey to expose the services as REST Web services, hence the @PATCH annotation. As to whether this is a controller or a service: it handles raw transfer data (the JsonNode), but to work efficiently it needs to be transactional (Changes made by the reader are flushed to the database when the transaction commits. Reading the object in the same transaction allows hibernate to dynamically update only the changed fields).

Sign up to request clarification or add additional context in comments.

1 Comment

Awesome info... I was checking this question because I had a similar problem in the past and ended up writing some specific code that was not going to be reusable. But I liked your answer!
0

If your User entity doesn't contains any security fields like login or password, you can simply use it as model attribute. In this case all fields will be updated automatically from the form inputs, those fields that are not supose to be updated, like id should be hidden fields on the form.

If you don't want to expose all your entity propeties to the presentation layer you can use pojo aka command to mapp all needed fields from user entity

BWT It is really bad practice to make your controller methods transactional. You should separate your application layers. You need to have service. This is the layer where @Transactional annotation belongs to. You do all the logic there before crud operations.

3 Comments

We have a seperated rich client project written in angular, we dont want to integrate with the presentation layer at all, and yes we have a service \controller \ dao layering , the transactional here was for the example's sake.
Ok, so it is a webservice? You can use @RequestBody
no we cant do that, we have a very complex permissions mechanism to enforce, change history, events (all involved in the update process)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.