【问题标题】:MapStruct: Map List of objects, when object is mapped from two objectsMapStruct:对象的映射列表,当对象从两个对象映射时
【发布时间】:2026-01-13 18:30:01
【问题描述】:

假设我有这样的映射:

@Mapping(source = "parentId", target = "parent.id")
Child map(ChildDto dto, Parent parent);

现在我需要将 ChildDto 列表映射到子列表,但它们都有相同的父级。我希望做这样的事情:

List<Child> map(List<ChildDto> dtoList, Parent parent);

但它不起作用。 有机会吗?

【问题讨论】:

    标签: java mapping mapstruct


    【解决方案1】:

    我找到了如何用装饰器来实现它,谢谢@Gunnar 这是一个实现:

    豆类

    public class Child {
        int id;
        String name;
    }
    public class Parent {
        int id;
        String name;
    }
    public class ChildDto {
        int id;
        String name;
        int parentId;
        String parentName;
    }
    // getters/settes ommited
    

    映射器

    @Mapper
    @DecoratedWith(ChildMapperDecorator.class)
    public abstract class ChildMapper {
        public static final ChildMapper INSTANCE = Mappers.getMapper(ChildMapper.class);
    
        @Mappings({
                @Mapping(target = "parentId", ignore = true),
                @Mapping(target = "parentName", ignore = true)
        })
        @Named("toDto")
        abstract ChildDto map(Child child);
    
        @Mappings({
                @Mapping(target = "id", ignore = true),
                @Mapping(target = "name", ignore = true),
                @Mapping(target = "parentId", source = "id"),
                @Mapping(target = "parentName", source = "name")
        })
        abstract ChildDto map(@MappingTarget ChildDto dto, Parent parent);
    
        @IterableMapping(qualifiedByName = "toDto") // won't work without it
        abstract List<ChildDto> map(List<Child> children);
    
        List<ChildDto> map(List<Child> children, Parent parent) {
            throw new UnsupportedOperationException("Not implemented");
        }
    }
    

    装饰器

    public abstract class ChildMapperDecorator extends ChildMapper {
        private final ChildMapper delegate;
    
        protected ChildMapperDecorator(ChildMapper delegate) {
            this.delegate = delegate;
        }
    
        @Override
        public List<ChildDto> map(List<Child> children, Parent parent) {
            List<ChildDto> dtoList = delegate.map(children);
            for (ChildDto childDto : dtoList) {
                delegate.map(childDto, parent);
            }
            return dtoList;
        }
    }
    

    我使用abstract class,而不是interface作为映射器,因为在interface的情况下,您无法排除生成方法map(List&lt;Child&gt; children, Parent parent),并且生成的代码在编译时无效。

    【讨论】:

    • 这个我试过了,还是不行。就我而言,我有 dto 与一组相同 dto 的孩子
    【解决方案2】:

    按照 Gunnar 的建议,我使用了 @AfterMapping

    @AfterMapping
    public void afterDtoToEntity(final QuestionnaireDTO dto, @MappingTarget final Questionnaire entity) {
      entity.getQuestions().stream().forEach(question -> question.setQuestionnaire(entity));
    }
    

    这确保所有问题都链接到同一个问卷实体。这是避免在创建具有子列表的新父实体时出现 JPA 错误 save the transient instance before flushing 的解决方案的最后一部分。

    【讨论】:

      【解决方案3】:

      就目前的情况而言,这不可能开箱即用。您可以使用装饰器或后映射方法将父对象设置为之后的所有子对象。

      【讨论】: