1

So let's say i have a UserDTO.

@Getter
@Setter
public User
{
  private int id;
  private String name;
  private Integer salary;
  private Integer department_id
}

I want to update info about my user partially, via patch request, send id of a person (requiered), and other data, that can be sent partially.

Let's say i have a (2, "Dave", 10, null) user in my db. I'm sending a request, body of request is a simple json: {"id":2, "salary":20} And i want to use that info to update the part of the info about the person, not him entirely. The problem is - deserializer puts null's in field that are missing, in other words, he treats my json as if it was: {"id":2, "salary":20, "name":null, "department_id":null} and i guess, it is not what i wanna do. A straight mapping could not get the job done, since he treats MISSING fields as EMPTY fields, which are not the same things, like an empty box, and abscence of it. That makes me think that putting body of a request in a UserDTO is not a good decision.

So even a custom mapper from json to DTO won't change a thing. What i want to do is to:

  • Extract the user from db by id
  • Update all the fields that are present in json
  • Save the updated user in db

If it is correct - i would be happy to know how to do such a thing. Otherwise - i am open to any kind of solutions.

  • You can use `Optional` class. Take a look at [How to deserialize json and distinguish `null` from the absence of a value](https://stackoverflow.com/a/74894437/51591) – Michał Ziober Jun 20 '23 at 23:10

3 Answers3

1

You might want to check out the specification RCF6902 in https://datatracker.ietf.org/doc/html/rfc6902 which gives a different approach to the PATCH request.

Basically you will be sending a list of operations to apply to the specified resource instead of the resource itself. For example if you had to update the name of your user instead of sending a

{
   "name": "New Name"
}

you would be sending

[
  { "op": "replace", "path": "name", "value": "New Name" }
]

It's powerful because it's:

  • flexible
  • scalable
  • can have centralized handling

I personally prefer this approach when building REST micro-services, and I never regretted this decision.

EDIT: I suggest also this article to read https://medium.com/@isuru89/a-better-way-to-implement-http-patch-operation-in-rest-apis-721396ac82bf

L_Cleo
  • 1,073
  • 1
  • 10
  • 26
0

The answer is simple. I totally forgot that Optional<> exists, and can take a role of a mentioned box. So if value was not passed at all, the field itself will be null. But if it was passed, and was null - the field will be Optional.empty

0

I Used Reflection to solve similar problem, so basically I looked up every field and updated the fields if the data is not null in the newer object.

        data.forEach((key, val) -> {
            if (val != null) {
                Field field = ReflectionUtils.findField(clazz, (String) key);
                if (field != null) {
                    field.setAccessible(true);
                    ReflectionUtils.setField(field, existingObj, val);
                }
            }
        });

though in my case I had used Map<Object, Object> data to fetch data from the request.

    public ResponseEntity<Object> patch(@RequestBody Map<Object, Object> data, @PathVariable Integer entityId)

Hope it helps.

Dave Ranjan
  • 2,966
  • 24
  • 55