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

SpringBoot中PatchMapping实现部分字段更新问题求助

Spring Boot 中使用 @PatchMapping 实现用户部分更新的正确方式

问题场景

作为Spring Boot初学者,学习过程中未接触@PatchMapping相关内容,当前只能通过冗余的if-else判断字段是否为空来实现用户数据的部分更新,需要更简洁规范的实现方案。


方案一:优化手动字段映射(精简if-else逻辑)

保留手动控制的灵活性,但去掉冗余的else分支,仅在DTO字段非空时更新实体对应字段:

修改UserServiceImpl的updateByid方法:

@Override
public Integer updateByid(Integer id, UserDto dto) {
    User user = userRepository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("未找到ID为" + id + "的用户"));
            
    // 仅更新DTO中不为null的字段
    if (dto.getCivility() != null) user.setCivility(dto.getCivility());
    if (dto.getLastname() != null) user.setLastname(dto.getLastname());
    if (dto.getFirstname() != null) user.setFirstname(dto.getFirstname());
    if (dto.getEmail() != null) user.setEmail(dto.getEmail());
    if (dto.getPassword() != null) user.setPassword(dto.getPassword()); // 注意:实际项目需加密密码
    if (dto.getPhone() != null) user.setPhone(dto.getPhone());
    if (dto.getBirth_day() != null) user.setBirth_day(dto.getBirth_day());
    
    userRepository.save(user);
    return user.getId();
}

方案二:使用Spring BeanUtils自动拷贝非空字段

借助Spring的BeanUtils.copyProperties方法,自动忽略DTO中的null值字段,简化代码:

首先导入依赖类:

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import java.beans.PropertyDescriptor;
import java.util.HashSet;
import java.util.Set;

然后修改UserServiceImpl的方法:

@Override
public Integer updateByid(Integer id, UserDto dto) {
    User user = userRepository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("未找到ID为" + id + "的用户"));
            
    // 拷贝DTO非空字段到实体,自动忽略null值
    BeanUtils.copyProperties(dto, user, getNullPropertyNames(dto));
    
    userRepository.save(user);
    return user.getId();
}

// 辅助方法:获取DTO中为null的属性名,用于拷贝时忽略
private String[] getNullPropertyNames(Object source) {
    BeanWrapper src = new BeanWrapperImpl(source);
    PropertyDescriptor[] pds = src.getPropertyDescriptors();
    
    Set<String> emptyNames = new HashSet<>();
    for (PropertyDescriptor pd : pds) {
        Object srcValue = src.getPropertyValue(pd.getName());
        if (srcValue == null) emptyNames.add(pd.getName());
    }
    return emptyNames.toArray(new String[0]);
}

方案三:使用JSON Patch(符合REST规范的标准方案)

如果要严格遵循RESTful规范,推荐使用JSON Patch(RFC 6902)实现标准的部分更新:

  1. 添加Maven依赖:
<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-json-patch</artifactId>
</dependency>
  1. 修改UserController接口,接收JsonPatch对象:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jsonpatch.JsonPatch;
import com.fasterxml.jackson.module.jsonpatch.JsonPatchException;

@PatchMapping("/update/{user-id}")
public ResponseEntity<UserDto> updateAccount(
        @PathVariable("user-id") Integer userId,
        @RequestBody JsonPatch patch
) throws JsonPatchException {
    User user = userRepository.findById(userId)
            .orElseThrow(() -> new EntityNotFoundException("未找到ID为" + userId + "的用户"));
            
    // 将实体转为DTO并应用Patch
    UserDto userDto = UserDto.fromEntity(user);
    ObjectMapper objectMapper = new ObjectMapper();
    JsonNode patched = patch.apply(objectMapper.convertValue(userDto, JsonNode.class));
    UserDto updatedDto = objectMapper.treeToValue(patched, UserDto.class);
    
    // 转换回实体并保存(确保ID不变)
    User updatedUser = UserDto.toEntity(updatedDto);
    updatedUser.setId(userId);
    userRepository.save(updatedUser);
    
    return ResponseEntity.ok(UserDto.fromEntity(updatedUser));
}

前端需发送符合JSON Patch规范的请求体,示例:

[
    {"op": "replace", "path": "/email", "value": "new-user@example.com"},
    {"op": "replace", "path": "/phone", "value": "13800138000"}
]

注意事项

  • 密码安全:更新密码时必须先加密(如使用BCrypt),禁止存储明文。
  • 字段校验:建议在UserDto上添加JSR-380注解(@Email、@Pattern等)做合法性校验。
  • 并发控制:高并发场景下,需在User实体添加@Version字段实现乐观锁,避免更新冲突。

内容的提问来源于stack exchange,提问作者tony

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 08:55:23