You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

执行PATCH更新操作时如何正确使用DTO实现数据部分更新?

实现DTO部分更新(PATCH接口)的方案

你当前的代码首先存在两个基础问题要先修正:

  1. 路径变量@PatchMapping("/{user}/edit")和接收参数@PathVariable long id名称不匹配,需要统一改成@PatchMapping("/{id}/edit"),否则参数绑定会失败
  2. 你查出来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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 21:57:01