0

Here is an example DTO

@Getter
@Setter
public class TestDto {
    private Long id;
    private String name;
    private String sex;
}

Say I have this object stored on the server: {"id":1, "name": "alex", "sex": "M"}

How can I send a request that only updates the "name" portion of the object?

Perhaps I could send this: {"id":1, "name":"adam"}

Such that the object will change to this: {"id":1, "name": "adam", "sex": "M"}

I also need the ability to set a field to null (i.e. clear the contents of a field).

In this case I would like to send {"id":1, "name":"adam", "sex":null}

To have the stored DTO change to {"id":1, "name": "adam", "sex":null}

How can I do this using java, spring boot, etc.?

I know the way to use:

@PutMapping
public TestDto update(Map<String, Object>map){ ... }

but I also need to some validation such that if I pass {"id":"1AA" ... } I get a serialization exception.

Ps.Find first step of this magic -> 1.Before path TestDto throu Rest - need to clear Type like this

Object body = testDto;

if will help you to get an Object with field what you want on server and then you'll be able to detect list of fieds to update

  • There are several mapping libraries (e.g. http://modelmapper.org) that can update fields of existing objects from other similar objects. That would allow you to define [validation](https://stackoverflow.com/questions/3595160/what-does-the-valid-annotation-indicate-in-spring) on TestDto as required and would allow you to merge data when it's for example `!= null`. Or you can simply write out the code to copy the present properties into the existing object (e.g. a `updateFrom(TestDto other)` method) – zapl Dec 20 '22 at 19:32
  • the question was how to detect that field is not passed from client - serializer will init this field with null and that its not what i need - i need set null value if clien pass null and do not set anything if client do not pass field – java developer 111 Dec 20 '22 at 19:41
  • Ah, sorry misunderstood the null part. Yeah, that's tricky, maps or defining a structure that contains explicit information about adding / removing fields, like `{ set: TestDto; remove: TestDto; }` – zapl Dec 20 '22 at 19:49
  • it may be looks like this @PutMapping public TestDto update(@RequestBody Mapmap){ val oldValue = cash.get((Long) (map.get("id"))); if(map.containsKey("name")) oldValue.setName((String) map.get("name")); if(map.containsKey("sex")) oldValue.setName((String) map.get("sex")); return cash.put(oldValue.getId(), oldValue); } – java developer 111 Dec 20 '22 at 19:53

3 Answers3

0

Instead of attempting to detect absent vs null value, consider defining an update object that includes a list of fields to be updated.

Such an object might look like this:

@Getter
@Setter
public class UpdateObject
{
    private long id; // ID of the object to be updated.
    private TestDto updates; // an object that contains the new values for the fields.
    private List<String> updateFields; // a list of fields to be updated.
}

Here is some Json

{
    "id": 1,
    "updates":
    {
        "name": "blem",
        "sex": null
    },
    "updateFields": ["name", "sex"]
}
DwB
  • 37,124
  • 11
  • 56
  • 82
0

if i understood right you just send request to the server with different fields. With @ModelAttribute annotation you can send your body in json format. if you send only one/two field or how you want {"id":1, "name":"adam"}, due to spring data jpa you can update your model in db. (in this case your field sex will be null and you need to create some manipulation for checking it kind of Mapstruct - convert your dto in other model plus checking null/not null fields). Better create default value for sex field if you want to saving not M and not FM values. null bad practice, in the future it will be bad joke for you.

@Getter
public Enum Sex { private MALE, private FEMALE, private DEFAULT }

  • no you dnt understand - i need to 3 states - null, not null, do not pass.Thant means if i pass name = null , name should be updeted with null , if i do not pass name it will stay like it was and if i pass name = "alex" it should be changed to alex, but serializer init the fields without value as null and this is what i want to change - may be using some custom serializer – java developer 111 Dec 20 '22 at 20:43
0

Ok guys finally fount how to do this

1.Client side - > path your testDto as Object, not as TestDto.class

Object payLoad = testDto;
template.postForObject("url", payload);

2.Server side - >

@RestController
@RequestMapping("/test")
public class TestController {

    private final Map<Long, TestDto> cash = new HashMap<>();
    private final ObjectMapper mapper = new ObjectMapper();

    @PostMapping
    public TestDto create(@RequestBody TestDto dto) {
        return cash.computeIfAbsent(dto.getId(), v -> dto);

    }

    @PutMapping("/{id}")
    public TestDto update(@PathVariable Long id, @RequestBody String json) throws JsonProcessingException {
        val oldValue = cash.get(id);
        mapper.readerForUpdating(oldValue).readValue(json);
        return cash.put(oldValue.getId(), oldValue);
    }

}

this hint let you update only field that client really changed