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

如何在Spring Boot中实现DTO部分更新、空值设置及参数校验

Spring Boot实现部分字段更新及类型校验

问题场景

现有如下DTO:

@Getter
@Setter
public class TestDto {
    private Long id;
    private String name;
    private String sex;
}

服务器存储的初始对象为:

{"id":1, "name": "alex", "sex": "M"}

需要实现:

  • 发送部分字段请求(如{"id":1, "name":"adam"}),仅更新指定字段,未传递的字段保留原值
  • 支持主动传递null清空字段(如{"id":1, "name":"adam", "sex":null}),将对应字段设为null
  • 字段类型校验:若传入非法类型(如id传"1AA"),触发序列化异常并返回错误

实现方案

1. 客户端请求构造

若客户端为Java环境,按提示将TestDto转为Object发送,确保仅序列化已设置的字段:

// 构造更新对象,仅设置需要修改的字段
TestDto updateDto = new TestDto();
updateDto.setId(1L);
updateDto.setName("adam");
// 若要清空sex字段,添加:updateDto.setSex(null);

// 转为Object类型,配合Jackson配置仅序列化已设置字段
Object requestBody = updateDto;

// 发送请求(推荐用PATCH语义更贴合部分更新)
restTemplate.patchForObject("/test", requestBody, TestDto.class);

2. 服务器端核心实现

全局异常处理(捕获类型错误)

添加全局异常处理器,统一处理类型校验失败及业务异常:

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler({JsonProcessingException.class, NumberFormatException.class})
    public ResponseEntity<String> handleInvalidFieldType(Exception e) {
        return ResponseEntity.badRequest().body("字段类型错误:" + e.getMessage());
    }

    @ExceptionHandler({IllegalArgumentException.class, RuntimeException.class})
    public ResponseEntity<String> handleBusinessException(Exception e) {
        return ResponseEntity.badRequest().body(e.getMessage());
    }
}

更新接口实现

用JsonNode接收请求体,既可以明确识别客户端传递的字段,又能通过Jackson完成类型校验:

@RestController
@RequestMapping("/test")
public class TestController {
    // 模拟数据库存储
    private final Map<Long, TestDto> testStore = new ConcurrentHashMap<>();

    // 初始化测试数据
    public TestController() {
        TestDto initDto = new TestDto();
        initDto.setId(1L);
        initDto.setName("alex");
        initDto.setSex("M");
        testStore.put(1L, initDto);
    }

    @PatchMapping
    public TestDto updateTest(@RequestBody JsonNode requestNode) {
        // 1. 校验必填字段id
        if (!requestNode.has("id")) {
            throw new IllegalArgumentException("id不能为空");
        }

        // 2. 解析id并校验类型
        Long id;
        try {
            id = requestNode.get("id").asLong();
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("id必须为有效数字");
        }

        // 3. 查询原对象
        TestDto originalDto = testStore.get(id);
        if (originalDto == null) {
            throw new RuntimeException("目标对象不存在");
        }

        // 4. 将请求体绑定到TestDto,完成全局类型校验
        ObjectMapper objectMapper = new ObjectMapper();
        TestDto updateDto;
        try {
            updateDto = objectMapper.treeToValue(requestNode, TestDto.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("字段类型不匹配", e);
        }

        // 5. 获取客户端实际传递的字段列表
        Set<String> passedFields = new HashSet<>();
        Iterator<String> fieldNames = requestNode.fieldNames();
        while (fieldNames.hasNext()) {
            passedFields.add(fieldNames.next());
        }

        // 6. 仅更新客户端传递的字段(包括主动传null的)
        Field[] fields = TestDto.class.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String fieldName = field.getName();
            if (passedFields.contains(fieldName)) {
                try {
                    Object value = field.get(updateDto);
                    field.set(originalDto, value);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("字段更新失败", e);
                }
            }
        }

        // 7. 保存更新(模拟数据库操作)
        testStore.put(id, originalDto);
        return originalDto;
    }
}

3. 可选:客户端Jackson配置

如果客户端是Spring Boot应用,配置Jackson仅序列化已设置的字段,避免未修改字段出现在请求中:

@Configuration
public class JacksonConfig {
    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper()
                // 仅序列化非默认值的字段(包装类默认值为null,未设置的字段不会被序列化)
                .setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 19:05:19