我试图在Spring Rest Controller中使用PUT请求方法部分更新实体时区分空值和未提供值。

以以下实体为例:

@Entity
private class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /* let's assume the following attributes may be null */
    private String firstName;
    private String lastName;

    /* getters and setters ... */
}

我的人资料库(Spring数据):
@Repository
public interface PersonRepository extends CrudRepository<Person, Long> {
}

我使用的DTO:
private class PersonDTO {
    private String firstName;
    private String lastName;

    /* getters and setters ... */
}

我的Spring RestController:
@RestController
@RequestMapping("/api/people")
public class PersonController {

    @Autowired
    private PersonRepository people;

    @Transactional
    @RequestMapping(path = "/{personId}", method = RequestMethod.PUT)
    public ResponseEntity<?> update(
            @PathVariable String personId,
            @RequestBody PersonDTO dto) {

        // get the entity by ID
        Person p = people.findOne(personId); // we assume it exists

        // update ONLY entity attributes that have been defined
        if(/* dto.getFirstName is defined */)
            p.setFirstName = dto.getFirstName;

        if(/* dto.getLastName is defined */)
            p.setLastName = dto.getLastName;

        return ResponseEntity.ok(p);
    }
}

请求缺少属性
{"firstName": "John"}

预期的行为:更新firstName= "John"(保留lastName不变)。

带空属性的请求
{"firstName": "John", "lastName": null}

预期的行为:更新firstName="John"并设置lastName=null

我无法区分这两种情况,因为DTO中的lastName始终由Jackson设置为null

注意:
我知道REST最佳实践(RFC 6902)建议使用PATCH而不是PUT进行部分更新,但是在我的特定情况下,我需要使用PUT。

最佳答案

实际上,如果忽略验证,您可以像这样解决您的问题。

   public class BusDto {
       private Map<String, Object> changedAttrs = new HashMap<>();

       /* getter and setter */
   }
  • 首先,为您的dto编写一个父类(super class),例如BusDto。
  • 其次,将您的d更改为扩展父类(super class),并更改
    dto的set方法,将属性名称和值放入
    changedAttrs(因为当
    属性具有值(无论是否为null)。
  • 第三,遍历 map 。
  • 10-08 10:47
    查看更多