Spring Boot RestAPI PATCH部分更新时未传递字段变为null问题
问题原因
- 反序列化阶段:你直接用
Warehouse实体类接收PATCH请求的@RequestBody,Jackson反序列化JSON时,请求中未携带的字段会赋值为实体的初始值:你代码里currentCapacity、cargosList、status都设置了默认初始值,所以反序列化后不会为null;而maxCapacity没有设置初始值,因此反序列化得到的请求参数对象中maxCapacity为null。 - 业务逻辑阶段:推测你的
warehouseService.partialUpdate方法没有做「仅更新请求传入字段」的处理,要么直接将请求参数对象全量覆盖数据库原有记录,要么直接保存了反序列化得到的临时对象,导致原有maxCapacity值被null覆盖。
解决方案
方案1:手动判断非空字段覆盖(最稳妥,适合字段较少的场景)
先查询数据库中已存在的持久化对象,仅把请求参数中不为null的字段覆盖到持久化对象上,再保存持久化对象即可:
// WarehouseService 中 partialUpdate 实现示例 public void partialUpdate(Integer id, Warehouse updateReq) { // 先查询数据库中已有记录 Warehouse existingWarehouse = warehouseRepository.findById(id) .orElseThrow(() -> new RuntimeException("对应仓库不存在")); // 仅覆盖非空的请求字段 if (updateReq.getLocation() != null) { existingWarehouse.setLocation(updateReq.getLocation()); } if (updateReq.getMaxCapacity() != null) { existingWarehouse.setMaxCapacity(updateReq.getMaxCapacity()); } if (updateReq.getStatus() != null) { existingWarehouse.setStatus(updateReq.getStatus()); } // 其他需要支持更新的字段同理添加判断逻辑 warehouseRepository.save(existingWarehouse); }
建议同时给Warehouse实体类添加@DynamicUpdate注解,Hibernate会生成仅包含变更字段的UPDATE语句,提升更新效率:
@Entity(name = "warehouses") @DynamicUpdate // 新增该注解 public class Warehouse { // 原有字段和注解保持不变 }
方案2:用Map接收请求参数(更灵活,适合字段多、变更频繁的场景)
Controller层直接用Map接收所有传入的更新字段,避免反序列化时未传字段被赋值为null的问题,再按需赋值给持久化对象:
// Controller层修改 @PatchMapping("/{id}") public void partialUpdate(@PathVariable Integer id, @RequestBody Map<String, Object> updateFields){ warehouseService.partialUpdate(id, updateFields); } // Service层实现示例 public void partialUpdate(Integer id, Map<String, Object> updateFields) { Warehouse existingWarehouse = warehouseRepository.findById(id) .orElseThrow(() -> new RuntimeException("对应仓库不存在")); updateFields.forEach((fieldName, fieldValue) -> { switch (fieldName) { case "location": existingWarehouse.setLocation((String) fieldValue); break; case "maxCapacity": existingWarehouse.setMaxCapacity((Integer) fieldValue); break; case "status": existingWarehouse.setStatus(WarehouseStatus.valueOf((String) fieldValue)); break; // 其他需要支持更新的字段同理添加分支 } }); warehouseRepository.save(existingWarehouse); }
方案3:工具类自动拷贝非空字段(减少重复代码)
如果字段较多不想手动写判断逻辑,可以借助Spring的BeanUtils工具类,自定义忽略null字段的拷贝逻辑:
// 自定义字段拷贝工具方法 public static void copyNonNullProperties(Object source, Object target) { BeanUtils.copyProperties(source, target, getNullFieldNames(source)); } private static String[] getNullFieldNames(Object source) { BeanWrapper srcBean = new BeanWrapperImpl(source); java.beans.PropertyDescriptor[] allFields = srcBean.getPropertyDescriptors(); Set<String> nullFieldNames = new HashSet<>(); for (java.beans.PropertyDescriptor field : allFields) { if (srcBean.getPropertyValue(field.getName()) == null) { nullFieldNames.add(field.getName()); } } return nullFieldNames.toArray(new String[0]); } // Service层调用示例 public void partialUpdate(Integer id, Warehouse updateReq) { Warehouse existingWarehouse = warehouseRepository.findById(id) .orElseThrow(() -> new RuntimeException("对应仓库不存在")); // 自动拷贝所有非空字段 copyNonNullProperties(updateReq, existingWarehouse); warehouseRepository.save(existingWarehouse); }
内容的提问来源于stack exchange,提问作者Erick Caron
相关产品推荐
相关产品推荐

