如何在Mapstruct多映射器场景下按条件置空指定字段?
问题说明
需求:当Shipment的status等于Status.Opened时,需将ShipmentResponseDto.cargo.packaging.lastMeasurements的firstField和secondField置为null,逻辑示例如下:
if(shipment.status == Status.Opened){ ShipmentResponseDto.cargo.packaging.lastMeasurements.firstField = null; ShipmentResponseDto.cargo.packaging.lastMeasurements.secondField = null; }
当前使用ShipmentMapper与CargoPackagingMapper组合映射的方式未实现该逻辑,相关代码结构如下:
@Mapper(uses = { CargoPackagingMapper.class }) public interface ShipmentMapper extends BasicMapper<ShipmentResponseDto, Shipment> { } @Mapper(uses = {PackagingTypeMapper.class}) public abstract class CargoPackagingMapper implements BasicMapper<CargoPackagingResponseDto, Packaging> { @Override @Mappings(...) public abstract CargoPackagingResponseDto toDto(Packaging source); } public class Shipment { // 其他字段 @Enumerated(EnumType.STRING) private Status status; private Set<ShipmentCargo> cargo = new HashSet<>(); // 其他字段 } public class ShipmentCargo { // 其他字段 @ManyToOne private Packaging packaging; // 其他字段 } public class Packaging { // 其他字段 private PackagingLastMeasurements lastMeasurements; // 其他字段 }
实现方案
核心思路是将上层Shipment的status作为上下文参数传递给下层的CargoPackagingMapper,让子映射器能根据状态判断是否需要修改字段值,具体实现如下:
1. 修改CargoPackagingMapper,添加带状态参数的映射方法
重载toDto方法接收Shipment的status,并通过@AfterMapping在映射完成后处理字段置空逻辑:
@Mapper(uses = {PackagingTypeMapper.class}) public abstract class CargoPackagingMapper implements BasicMapper<CargoPackagingResponseDto, Packaging> { // 暴露Mapper实例,供上层调用 public static final CargoPackagingMapper INSTANCE = Mappers.getMapper(CargoPackagingMapper.class); // 原有基础映射方法 @Override @Mappings({ // 保留原有字段映射配置 }) public abstract CargoPackagingResponseDto toDto(Packaging source); // 重载带Shipment状态参数的映射方法 @Mappings({ // 继承原有全部映射规则 @Mapping(target = ".", source = "source") }) public abstract CargoPackagingResponseDto toDto(Packaging source, Status shipmentStatus); // 映射完成后置空指定字段的逻辑 @AfterMapping protected void handleOpenedStatusMeasurements( @MappingTarget CargoPackagingResponseDto dto, Packaging source, Status shipmentStatus ) { if (Status.Opened.equals(shipmentStatus)) { // 判空避免NPE if (dto.getPackaging() != null && dto.getPackaging().getLastMeasurements() != null) { dto.getPackaging().getLastMeasurements().setFirstField(null); dto.getPackaging().getLastMeasurements().setSecondField(null); } } } }
2. 修改ShipmentMapper,传递状态参数给子映射器
自定义集合映射逻辑,遍历ShipmentCargo时将Shipment的status传递给CargoPackagingMapper的重载方法:
@Mapper(uses = {CargoPackagingMapper.class}) public interface ShipmentMapper extends BasicMapper<ShipmentResponseDto, Shipment> { @Override @Mapping( target = "cargo", source = "cargo", qualifiedByName = "mapCargoWithShipmentStatus" ) ShipmentResponseDto toDto(Shipment source); // 自定义集合映射方法,传递Shipment状态 @Named("mapCargoWithShipmentStatus") default List<CargoPackagingResponseDto> mapCargoWithShipmentStatus( Set<ShipmentCargo> shipmentCargos, Shipment source ) { return shipmentCargos.stream() .map(shipmentCargo -> CargoPackagingMapper.INSTANCE.toDto( shipmentCargo.getPackaging(), source.getStatus() )) .collect(Collectors.toList()); // 如果ShipmentResponseDto的cargo是Set类型,替换为Collectors.toSet() } }
说明
- 通过传递上下文参数(Shipment的status),让子映射器能感知上层对象的状态,实现条件映射逻辑。
- 使用
@AfterMapping确保在基础映射完成后再修改字段值,避免覆盖原有映射结果。 - 增加判空逻辑防止空指针异常,提升代码健壮性。
内容的提问来源于stack exchange,提问作者Самир Шахмурадлы
相关产品推荐
相关产品推荐

