本文介绍如何在 mapstruct 中实现“仅当目标对象的某个字段不为 null 时,才将源字段映射过去”,避免覆盖已有值,适用于增量更新、dto 合并等场景。
本文介绍如何在 mapstruct 中实现“仅当目标对象的某个字段不为 null 时,才将源字段映射过去”,避免覆盖已有值,适用于增量更新、dto 合并等场景。
在使用 MapStruct 进行对象映射(尤其是 @MappingTarget 增量更新)时,一个常见需求是:仅当目标对象的某字段已有值(非 null)时,才跳过源字段的赋值;反之,若目标字段为 null,则允许从源对象映射新值。但需注意:题中原始诉求“if engine is null, I don't want to map it”实际语义易歧义——通常开发者真正意图是:“当目标 car.engine 已存在(非 null)时,不要用 carDto.device 覆盖它”,即“非空保护式映射”。
MapStruct 本身不支持 @Mapping(whenTargetNotNull = true) 这类原生条件,但可通过组合策略优雅实现:
@Mapperpublic interface CarMapper { // 先忽略 engine 字段的自动映射 @Mapping(target = "engine", ignore = true) void updateCarFromDto(CarDto carDto, @MappingTarget Car car); // 在映射完成后,按业务逻辑补充处理 @AfterMapping default void handleEngineMapping(CarDto carDto, @MappingTarget Car car) { // 安全校验:防止空指针 if (car == null || carDto == null) { return; } // 关键逻辑:仅当目标 engine 为 null 时,才赋予新值 if (car.getEngine() == null && carDto.getDevice() != null) { car.setEngine(carDto.getDevice()); } // 注:若需“非空时才赋值”,则改为 `car.getEngine() != null` }}
? 提示:上述示例中 if (car.getEngine() == null) 表示“目标为空才映射”,符合多数增量更新场景(如部分字段补全);若需反向逻辑(目标非空才映射),只需调整条件即可。
若项目已升级至 MapStruct 1.5 或更高版本,可利用 @Condition 实现更声明式的条件映射:
@Mapperpublic interface CarMapper { @Mapping(target = "engine", source = "device") void updateCarFromDto(CarDto carDto, @MappingTarget Car car); @Condition default boolean shouldMapEngine(CarDto carDto, @MappingTarget Car car) { return car != null && car.getEngine() == null && carDto != null && carDto.getDevice() != null; }}
此时 shouldMapEngine 将自动被 MapStruct 调用,仅当返回 true 时才执行 device → engine 映射。此方式更简洁、更贴近函数式表达,推荐新项目优先采用。
总之,MapStruct 的条件映射虽无开箱即用的 whenTargetNull 属性,但通过 @AfterMapping 或 @Condition,均可清晰、安全、可维护地实现业务所需的空值保护逻辑。