MapStruct nullValuePropertyMappingStrategy 使用详解

发表于 2025-10-09 22:24:19 分类于 默认分类 阅读量 64

MapStruct nullValuePropertyMappingStrategy 使用详解

在使用 MapStruct 进行 DTO 与实体对象映射时,经常会遇到字段为 null 时的处理问题。MapStruct 提供了 nullValuePropertyMappingStrategy 参数,用于控制 源对象字段为 null 时目标对象的映射行为。本文将详细解释三种常用策略:IGNORESET_TO_NULLSET_TO_DEFAULT,以及实际使用场景。


1. 配置位置

nullValuePropertyMappingStrategy 可以在 Mapper 接口全局配置,也可以在单个方法上配置:

import org.mapstruct.Mapper;
import org.mapstruct.NullValuePropertyMappingStrategy;
import org.mapstruct.factory.Mappers;

@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface UserUpdateDTOConverter {
    UserUpdateDTOConverter INSTANCE = Mappers.getMapper(UserUpdateDTOConverter.class);

    User toUser(UserUpdateDTO dto);

    UserExtension toUserExtension(UserUpdateDTO dto);
}

或者针对单个方法:

import org.mapstruct.BeanMapping;
import org.mapstruct.NullValuePropertyMappingStrategy;

@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_NULL)
UserExtension toUserExtension(UserUpdateDTO dto);

2. 三种策略详解

2.1 IGNORE(默认行为)

  • 含义:如果源字段为 null忽略映射,目标字段保持原值。
  • 使用场景:更新操作时,只覆盖非 null 字段,防止意外置空。
源字段目标字段结果字段
null123123
456123456

优点:安全,不会覆盖数据库已有值。 ❌ 缺点:无法通过 DTO 将字段置空。


2.2 SET_TO_NULL

  • 含义:如果源字段为 null将目标字段也设置为 null
  • 使用场景:需要明确将字段置空,例如用户取消某个扩展信息。
源字段目标字段结果字段
null123null
456123456

优点:可以把 null 映射过去,覆盖数据库原值。 ❌ 缺点:更新时需要小心,不然可能误把重要字段置空。


2.3 SET_TO_DEFAULT

  • 含义:如果源字段为 null将目标字段设置为 Java 默认值

    • 对象类型 → null
    • 基本类型 → 对应默认值(如 int → 0,boolean → false)
  • 使用场景:创建新对象时,确保字段有初始值。

源字段目标字段结果字段
null1230 (int)
null"abc"null
truefalsetrue

优点:确保字段有默认值。 ❌ 缺点:基本类型可能不符合业务逻辑,对对象类型效果不明显。


3. 使用建议

策略适用场景
IGNORE更新操作,保留数据库已有值
SET_TO_NULL更新操作,需要清空某些字段
SET_TO_DEFAULT新建对象,需要保证字段有默认值

4. 示例:用户扩展信息更新

@Transactional(rollbackFor = Exception.class)
public void updateUserExtension(UserUpdateDTO dto) {
    Long userId = dto.getId();
    if (userId == null) {
        throw new BusinessException("用户ID不能为空");
    }

    UserExtension existingExtension = getById(userId);
    UserExtension updatedExtension = UserUpdateDTOConverter.INSTANCE.toUserExtension(dto);

    if (existingExtension == null) {
        save(updatedExtension);
    } else {
        updateById(updatedExtension);
    }
}

如果在 UserUpdateDTOConverter 中使用 SET_TO_NULL,传入 DTO 的 null 字段会覆盖数据库原值,支持显式置空。


5. 总结

  • IGNORE:安全,不覆盖 null。
  • SET_TO_NULL:更新时覆盖 null,可置空字段。
  • SET_TO_DEFAULT:保证字段有默认值,适合新建对象。

通过合理选择 nullValuePropertyMappingStrategy,可以让 MapStruct 在 更新操作新建操作 中都符合业务逻辑,同时避免意外覆盖或丢失数据。