简体   繁体   中英

How can I ignore missing properties in my DTO objects with Spring MVC?

We're using Spring MVC 4 .

Say I have an entity called Customer . That entity has several properties and some allow nulls and others do not.

We also use a DTO object ( CustomerDTO ) that is passed in from remote clients to our @RestController via @RequestBody .

Here's the problem I'm having. Say the user passes in the following via a PUT :

{
    "id": 123,
    "name": "ACME",
    "desc": "Blah"
}

All is fine. But if the user ONLY wants to update the name , they pass in:

{
    "id": 123,
    "name": "ACME 2"
}

The customer now has a null in the desc which is allowed.

So my question is, how can I get the Spring/Hibernate to not even put desc in the update statement if it wasn't passed into the DTO?

The problem, I believe, is that Spring treats the following as the same thing:

{
    ...
    "desc": null,
    ...
}

{
    ...
    ...   <desc omitted>
}

Thanks

Based on Is it possible to update only a subset of attributes on an entity using Spring MVC with JPA? , it seems you basically have 2 viable options:

  1. Read the entity from the DB and update the specific fields
  2. Use @SessionAttibutes

Instead of implementing this by loading entity state separately, you could make use of Blaze-Persistence Updatable Entity Views which is a library for developing DTOs on top of JPA that also implements support for optimistic locking. Your use case should be supported already, though I don't have a very good integration for Spring WebMvc yet, so you will have to do some plumbing yourself for now. I have something in mind for this though, it's just a matter of time and interested parties until the integration will be smoother.

Updatable entity views allow to map a subset of entities and also only flush that subset back. Thanks to the use of dirty tracking, it knows exactly what changed, allowing for fine grained flushing.

So the idea for PATCH support , which is what you want here, is to just get an empty reference by id for an object. Being empty means, that it has no data ie all null values. The dirty tracking assumes the initial state is all null then. You can simply map a request payload onto this object, if a value is null, it won't recognize it as being changed, thus ignore it. If anything non-null was set, it determines that such a field is dirty and on flush, only flushes dirty values.

I haven't tried it myself yet, but you could do something like this

// Create reference for the id, the object is empty i.e. all null except for the id
CustomerDTO dto = entityViewManager.getReference(CustomerDTO.class, someId);
// Map the payload on the DTO which will call setFoo(null) but that's ok, because that isn't considered being dirty
jsonMapper.map(requestPayload, dto);
// Flush dirty changes i.e. non-null values
entityViewManager.update(entityManager, dto);

The executed update query when using the PARTIAL flush mode will only contain set clauses for properties with non-null values. The DTO would look like this

@EntityView(Customer.class)
@UpdatableEntityView(mode = FlushMode.PARTIAL)
public interface CustomerDTO {
  @IdMapping Integer getId();
  String getName();
  void setName(String name);
  String getDesc();
  void setDesc(String desc);
}

If nothing is dirty, it won't even execute a query.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM