使用MapStruct映射子类时父类嵌套对象字段丢失问题求助
在处理继承结构的类映射时,你遇到了子类Mapper未处理父类嵌套对象字段的问题——ChildMapperImpl仅映射了id和childName,但父类BaseDTO中的FullNameDTO name字段未被正确映射。这是因为MapStruct默认只会自动继承父类的基本类型字段映射,对于父类Mapper中定义的自定义嵌套字段映射逻辑不会自动复用,尤其是结合@SuperBuilder使用时。
解决方案一:复用BaseMapper的转换逻辑(推荐)
修改ChildMapper,显式调用BaseMapper的转换方法来处理父类字段,同时补充子类字段的映射:
@Mapper(uses = BaseMapper.class) public interface ChildMapper { // 显式指定父类嵌套字段的映射来源为entity,复用BaseMapper的逻辑 @Mapping(target = "name", source = "entity") ChildDTO toDTO(ChildEntity entity); // 定义默认方法,让MapStruct调用BaseMapper处理父类部分 default BaseDTO mapBaseFields(ChildEntity entity) { return BaseMapper.INSTANCE.toDTO(entity); } }
生成的ChildMapperImpl会自动调用mapBaseFields方法获取父类的name字段值,最终完整映射所有字段。
解决方案二:手动复用父类转换结果
如果希望逻辑更直观,可以在ChildMapper中手动调用BaseMapper的转换方法,然后通过Builder拼接子类字段:
@Mapper public interface ChildMapper { ChildMapper INSTANCE = Mappers.getMapper(ChildMapper.class); default ChildDTO toDTO(ChildEntity entity) { if (entity == null) { return null; } // 先转换父类字段 BaseDTO baseDTO = BaseMapper.INSTANCE.toDTO(entity); // 用Builder拼接子类字段 return ChildDTO.builder() .id(baseDTO.getId()) .name(baseDTO.getName()) .childName(entity.getChildName()) .build(); } }
解决方案三:显式声明父类嵌套字段的映射
如果不想依赖父类Mapper的复用,可以直接在ChildMapper中重复父类的映射规则:
@Mapper public interface ChildMapper { @Mapping(target = "name.firstName", source = "firstName") @Mapping(target = "name.lastName", source = "lastName") ChildDTO toDTO(ChildEntity entity); ChildMapper INSTANCE = Mappers.getMapper(ChildMapper.class); }
这种方式虽然可行,但会导致代码重复,不推荐在父类映射逻辑复杂时使用。
原因说明
MapStruct对于继承结构的自动映射仅针对直接对应的字段(比如父类的id是基本类型,子类DTO继承后可以直接映射),但对于父类Mapper中定义的自定义字段转换逻辑(比如将firstName/lastName组合成FullNameDTO),不会自动继承到子类Mapper中。结合@SuperBuilder时,MapStruct生成的Builder代码只会处理当前类的字段和父类的基本类型字段,不会触发父类Mapper的转换逻辑,因此需要显式指定或手动调用父类的映射方法。
内容的提问来源于stack exchange,提问作者Rohit Patel

