MapStruct nullValuePropertyMappingStrategy
使用详解
在使用 MapStruct 进行 DTO 与实体对象映射时,经常会遇到字段为 null
时的处理问题。MapStruct 提供了 nullValuePropertyMappingStrategy
参数,用于控制 源对象字段为 null
时目标对象的映射行为。本文将详细解释三种常用策略:IGNORE
、SET_TO_NULL
、SET_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 字段,防止意外置空。
源字段 | 目标字段 | 结果字段 |
---|---|---|
null | 123 | 123 |
456 | 123 | 456 |
✅ 优点:安全,不会覆盖数据库已有值。 ❌ 缺点:无法通过 DTO 将字段置空。
2.2 SET_TO_NULL
- 含义:如果源字段为
null
,将目标字段也设置为null
。 - 使用场景:需要明确将字段置空,例如用户取消某个扩展信息。
源字段 | 目标字段 | 结果字段 |
---|---|---|
null | 123 | null |
456 | 123 | 456 |
✅ 优点:可以把 null 映射过去,覆盖数据库原值。 ❌ 缺点:更新时需要小心,不然可能误把重要字段置空。
2.3 SET_TO_DEFAULT
-
含义:如果源字段为
null
,将目标字段设置为 Java 默认值。- 对象类型 →
null
- 基本类型 → 对应默认值(如
int
→ 0,boolean
→ false)
- 对象类型 →
-
使用场景:创建新对象时,确保字段有初始值。
源字段 | 目标字段 | 结果字段 |
---|---|---|
null | 123 | 0 (int) |
null | "abc" | null |
true | false | true |
✅ 优点:确保字段有默认值。 ❌ 缺点:基本类型可能不符合业务逻辑,对对象类型效果不明显。
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 在 更新操作 和 新建操作 中都符合业务逻辑,同时避免意外覆盖或丢失数据。