执行PATCH更新操作时如何正确使用DTO实现数据部分更新?
实现DTO部分更新(PATCH接口)的方案
你当前的代码首先存在两个基础问题要先修正:
- 路径变量
@PatchMapping("/{user}/edit")和接收参数@PathVariable long id名称不匹配,需要统一改成@PatchMapping("/{id}/edit"),否则参数绑定会失败 - 你查出来
User实体后没有把DTO的字段更新到实体上,直接调用update方法不会产生任何修改效果
下面是三种常用的实现方案:
方案1:手动字段判空赋值(最稳妥,无额外依赖)
直接对DTO的每个字段做非空判断,有值才覆盖到实体上,适合字段不多的场景:
@PatchMapping("/{id}/edit") public ResponseEntity<String> update(@RequestBody UserDto userDto, @PathVariable long id) { Optional<User> optionalUser = userService.getById(id); if (!optionalUser.isPresent()) { return ResponseEntity .badRequest() .body("用户不存在"); } User user = optionalUser.get(); // 非空字段才更新 if (userDto.getFirstName() != null) { user.setFirstName(userDto.getFirstName()); } if (userDto.getLastName() != null) { user.setLastName(userDto.getLastName()); } if (userDto.getDateOfBirth() != null) { user.setDateOfBirth(userDto.getDateOfBirth()); } if (userDto.getEducation() != null) { user.setEducation(userDto.getEducation()); } if (userDto.getAboutMe() != null) { user.setAboutMe(userDto.getAboutMe()); } userService.update(user); return ResponseEntity.ok().build(); }
方案2:借助Spring BeanUtils自动忽略空字段
适合字段较多的场景,不需要手动写每个字段的判断,先写一个工具方法获取DTO中所有null字段的名称,复制属性时忽略这些字段:
首先定义工具方法:
// 工具方法:获取对象中所有为null的字段名 public static String[] getNullPropertyNames (Object source) { final BeanWrapper src = new BeanWrapperImpl(source); java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors(); Set<String> emptyNames = new HashSet<>(); for(java.beans.PropertyDescriptor pd : pds) { Object srcValue = src.getPropertyValue(pd.getName()); if (srcValue == null) emptyNames.add(pd.getName()); } String[] result = new String[emptyNames.size()]; return emptyNames.toArray(result); }
然后接口中直接调用即可:
@PatchMapping("/{id}/edit") public ResponseEntity<String> update(@RequestBody UserDto userDto, @PathVariable long id) { Optional<User> optionalUser = userService.getById(id); if (!optionalUser.isPresent()) { return ResponseEntity .badRequest() .body("用户不存在"); } User user = optionalUser.get(); // 复制属性时忽略DTO里为null的字段 BeanUtils.copyProperties(userDto, user, getNullPropertyNames(userDto)); userService.update(user); return ResponseEntity.ok().build(); }
方案3:用MapStruct转换器实现(你猜想的转换器方案)
如果项目已经在用MapStruct做对象转换,直接写更新方法即可,性能比反射实现的BeanUtils高很多:
首先定义转换器Mapper:
@Mapper(componentModel = "spring") public interface UserMapper { // 把userDto的非空字段更新到已有的user实体上 void updateUserFromDto(UserDto userDto, @MappingTarget User user); }
然后接口里注入Mapper调用:
@Autowired private UserMapper userMapper; @PatchMapping("/{id}/edit") public ResponseEntity<String> update(@RequestBody UserDto userDto, @PathVariable long id) { Optional<User> optionalUser = userService.getById(id); if (!optionalUser.isPresent()) { return ResponseEntity .badRequest() .body("用户不存在"); } User user = optionalUser.get(); userMapper.updateUserFromDto(userDto, user); userService.update(user); return ResponseEntity.ok().build(); }
内容的提问来源于stack exchange,提问作者Vladislav Sedov
相关产品推荐
相关产品推荐

