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

Spring中如何通过@RequestBody判断JSON字段是否存在数据?

如何实现Patch请求的用户信息部分更新

场景说明

现有如下User实体类:

@Getter
@Setter
public class User {
    private int id;
    private String name;
    private Integer salary;
    private Integer department_id;
}

需求:通过Patch请求实现用户信息的部分更新,要求请求体必须包含id字段,其他字段可选择性传入。例如数据库中存在用户(2, "Dave", 10, null),当发送请求体{"id":2, "salary":20}时,仅更新该用户的薪资字段,而非将name和department_id设为null。

核心问题

直接将请求体反序列化为User对象时,缺失的字段会被设为null,导致误更新原数据库中的非空字段。

解决方案

核心思路是:先从数据库获取原用户对象,仅更新请求体中实际存在的字段,再保存回数据库。以下是两种可行实现方式:


方式一:使用Jackson JsonNode处理请求体

利用Jackson的JsonNode保留请求体的字段存在性信息,再通过ObjectMapper.updateValue方法仅更新存在的字段:

@PatchMapping("/users")
public ResponseEntity<User> patchUser(@RequestBody JsonNode requestNode) {
    // 校验必填id字段
    if (!requestNode.has("id")) {
        return ResponseEntity.badRequest().body(null);
    }
    Integer userId = requestNode.get("id").asInt();
    
    // 从数据库获取原用户对象
    User originalUser = userRepository.findById(userId)
            .orElseThrow(() -> new IllegalArgumentException("用户不存在"));
    
    // 仅更新请求体中存在的字段
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.updateValue(originalUser, requestNode);
    
    // 保存更新后的用户
    User updatedUser = userRepository.save(originalUser);
    return ResponseEntity.ok(updatedUser);
}

说明:updateValue方法会遍历JsonNode中的所有字段,仅对原对象中对应字段进行赋值,未在请求体中出现的字段会保持原对象的原值,完美区分“字段缺失”和“字段值为null”的场景。


方式二:通过Map接收请求体+反射更新字段

如果需要更精细化的字段控制(比如跳过主键修改、自定义字段校验),可以用Map<String, Object>接收请求体,再通过反射逐一更新字段:

@PatchMapping("/users")
public ResponseEntity<User> patchUser(@RequestBody Map<String, Object> requestMap) {
    // 校验必填id字段
    if (!requestMap.containsKey("id")) {
        return ResponseEntity.badRequest().body(null);
    }
    Integer userId = (Integer) requestMap.get("id");
    
    // 从数据库获取原用户对象
    User originalUser = userRepository.findById(userId)
            .orElseThrow(() -> new IllegalArgumentException("用户不存在"));
    
    // 遍历请求体字段,更新原对象
    requestMap.forEach((key, value) -> {
        // 跳过主键id,避免修改
        if ("id".equals(key)) {
            return;
        }
        try {
            Field field = User.class.getDeclaredField(key);
            field.setAccessible(true);
            // 赋值时自动处理类型转换(需确保请求值类型与字段匹配)
            field.set(originalUser, value);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new IllegalArgumentException("无效的字段:" + key);
        }
    });
    
    // 保存更新后的用户
    User updatedUser = userRepository.save(originalUser);
    return ResponseEntity.ok(updatedUser);
}

说明:这种方式可以灵活控制哪些字段允许更新,还能在赋值前添加自定义的校验逻辑(比如薪资不能为负数)。

注意事项

  1. 主键id建议设为不可修改,避免请求体传入新id导致数据混乱;
  2. 需对请求体中的字段值做合法性校验,比如数值范围、字符串格式等;
  3. 若使用Spring Data JPA,save方法会自动执行更新操作(因为原对象是从数据库查询的持久化实例)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 03:35:42