You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MapStruct配置:DTO所有属性为空时返回null而非空对象

问题背景

你现在有一个映射数据库视图的扁平实体类VwPlanningAmlControl,需要转成带嵌套UserPlanningDTO的PlanningAmlControlDTO。目前遇到的问题是:当映射UserPlanningDTO用到的所有源字段全为null时,MapStruct会返回一个所有属性为null的空UserPlanningDTO对象,而期望此时直接返回null。

现有代码

源实体类(核心关联字段展示)

public class VwPlanningAmlControl {
    // 其他业务字段省略
    // confirmedBy关联字段
    private Integer confirmedbyid;
    private String confirmedbyfirstname;
    private String confirmedbylastname;
    // rejectedBy关联字段
    private Integer rejectedbyid;
    private String rejectedbyfirstname;
    private String rejectedbylastname;
    // createdBy关联字段
    private Integer createdbyid;
    private String createdbyfirstname;
    private String createdbylastname;
    // modifiedBy关联字段
    private Integer updatedbyid;
    private String updatedbyfirstname;
    private String updatedbylastname;
}

目标DTO类

public class UserPlanningDTO {
    private Integer id;
    private String  firstName;
    private String  lastName;
}

public class PlanningAmlControlDTO extends EventAmlControlDTO {
    // 其他业务字段省略
    private UserPlanningDTO confirmedBy;
    private UserPlanningDTO rejectedBy;
    private UserPlanningDTO createdBy;
    private UserPlanningDTO modifiedBy;
}

现有映射配置

@Mapping(target = "id", source = "controlid")
// 其他普通业务字段映射省略
@Mapping(target = "confirmedBy.id", source = "confirmedbyid")
@Mapping(target = "confirmedBy.firstName", source = "confirmedbyfirstname")
@Mapping(target = "confirmedBy.lastName", source = "confirmedbylastname")
@Mapping(target = "rejectedBy.id", source = "rejectedbyid")
@Mapping(target = "rejectedBy.firstName", source = "rejectedbyfirstname")
@Mapping(target = "rejectedBy.lastName", source = "rejectedbylastname")
@Mapping(target = "createdBy.id", source = "createdbyid")
@Mapping(target = "createdBy.firstName", source = "createdbyfirstname")
@Mapping(target = "createdBy.lastName", source = "createdbylastname")
@Mapping(target = "modifiedBy.id", source = "updatedbyid")
@Mapping(target = "modifiedBy.firstName", source = "updatedbyfirstname")
@Mapping(target = "modifiedBy.lastName", source = "updatedbylastname")
PlanningAmlControlDTO toDto(VwPlanningAmlControl planningAmlControl);
解决方案

MapStruct默认只要源对象不为null,就会实例化嵌套的目标对象,不会自动检查所有映射属性是否全为空。我们可以通过自定义映射条件实现需求,推荐使用MapStruct提供的@Condition注解方案,优雅且易维护:

步骤1:自定义条件判断类

public class UserMappingCondition {
    @Condition
    public boolean needBuildUserDTO(VwPlanningAmlControl source, @TargetProperty String targetProperty) {
        switch (targetProperty) {
            case "confirmedBy":
                // 只要有一个关联字段不为空就需要构建DTO
                return source.getConfirmedbyid() != null 
                    || source.getConfirmedbyfirstname() != null
                    || source.getConfirmedbylastname() != null;
            case "rejectedBy":
                return source.getRejectedbyid() != null 
                    || source.getRejectedbyfirstname() != null
                    || source.getRejectedbylastname() != null;
            case "createdBy":
                return source.getCreatedbyid() != null 
                    || source.getCreatedbyfirstname() != null
                    || source.getCreatedbylastname() != null;
            case "modifiedBy":
                return source.getUpdatedbyid() != null 
                    || source.getUpdatedbyfirstname() != null
                    || source.getUpdatedbylastname() != null;
            default:
                return true;
        }
    }
}

步骤2:在Mapper中引入条件配置

@Mapper(
    uses = UserMappingCondition.class,
    nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL
)
public interface PlanningAmlControlMapper {
    // 原有所有@Mapping配置保持不变即可
    PlanningAmlControlDTO toDto(VwPlanningAmlControl planningAmlControl);
}

配置后MapStruct生成代码时,会先调用条件方法判断是否需要构建嵌套的UserPlanningDTO,如果所有关联字段全为null,就会直接给属性赋值为null,不会再实例化空对象。

其他可选方案(不推荐)

如果不想额外写条件类,也可以直接在@Mapping中用expression硬编码判断,缺点是代码冗余维护性差:

@Mapping(
    target = "confirmedBy", 
    expression = "java( planningAmlControl.getConfirmedbyid() == null && planningAmlControl.getConfirmedbyfirstname() == null && planningAmlControl.getConfirmedbylastname() == null ? null : new UserPlanningDTO(planningAmlControl.getConfirmedbyid(), planningAmlControl.getConfirmedbyfirstname(), planningAmlControl.getConfirmedbylastname()) )"
)
// 其他三个嵌套字段同理配置即可

内容的提问来源于stack exchange,提问作者usr-local-ΕΨΗΕΛΩΝ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 04:57:01