POST更新用户家属信息时createdAt字段被置空的问题排查
问题分析
更新已有家属信息时createdAt被置为null的核心原因:
- 前端更新操作不会传递
createdAt字段,导致DTO中的createdAt为null - MapStruct默认会将DTO的null值映射到实体对象,覆盖了从数据库查询出的原有
createdAt值 @PreUpdate仅负责更新updatedAt,不会处理createdAt的null覆盖问题
解决方案
方案一:修改MapStruct映射规则(推荐)
通过配置MapStruct忽略null值的属性映射,避免DTO中的null覆盖实体原有字段:
@Named("UserMapper") @Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface UserMapper { UserMapper mapper = Mappers.getMapper(UserMapper.class); UserDTO toDto(UserEntity entity); // 全局忽略null值映射 @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) UserEntity toEntity(UserDTO dto); List<UserDTO> toDto(List<UserEntity> entity); @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) List<UserEntity> toEntity(List<UserDTO> dto); }
或者针对单个字段精准控制:
@Mapping(target = "createdAt", ignore = true) UserEntity toEntity(UserDTO dto);
方案二:实体层添加字段保护
修改UserDependentsEntity的setCreatedAt方法,仅允许在字段为空时赋值:
public void setCreatedAt(LocalDateTime createdAt) { // 仅当当前createdAt为null时才赋值,更新操作保留原有创建时间 if (this.createdAt == null) { this.createdAt = createdAt; } }
方案三:服务层合并原有数据
在更新时手动合并原有家属的createdAt字段,避免直接覆盖列表:
public void updatePreferencesAndDependents(UUID userId, UpdatePreferencesDependentsDTO updateDTO) { var user = getUserById(userId); // 更新用户偏好字段 user.setReferralHospital(updateDTO.getReferralHospital()); user.setReferralHospitalId(updateDTO.getReferralHospitalId()); user.setAttendance(updateDTO.getAttendance()); user.setCoverage(updateDTO.getCoverage()); user.setAccommodation(updateDTO.getAccommodation()); user.setPartaking(updateDTO.getPartaking()); // 合并家属数据,保留原有createdAt List<UserDependentsDTO> existingDependents = user.getUserDependents(); List<UserDependentsDTO> updatedDependents = updateDTO.getUserDependents(); if (updatedDependents != null) { Map<UUID, UserDependentsDTO> existingMap = existingDependents.stream() .collect(Collectors.toMap(UserDependentsDTO::getUuid, d -> d)); List<UserDependentsDTO> mergedDependents = updatedDependents.stream() .map(dto -> { UserDependentsDTO existing = existingMap.get(dto.getUuid()); if (existing != null) { dto.setCreatedAt(existing.getCreatedAt()); } return dto; }) .collect(Collectors.toList()); user.setUserDependents(mergedDependents); } var entity = UserMapper.mapper.toEntity(user); repository.save(entity); }
方案对比
- 方案一:最简洁高效,利用MapStruct原生特性解决,无需额外业务逻辑,优先推荐
- 方案二:在实体层做兜底防护,避免任何场景下的意外覆盖,适合作为补充措施
- 方案三:灵活性最高,适合需要精细控制字段更新的复杂业务场景
内容的提问来源于stack exchange,提问作者Ferzinia
相关产品推荐
相关产品推荐

