如何使用 MapStruct 将实体和实体列表映射到具有嵌套列表的单个 DTO?

How to map entity and list of entities to a single DTO with nested list by using MapStruct?

我需要使用 MapStruct 将一些实体和实体列表转换为单个 DTO。此 DTO 包含嵌套列表。

比方说,我有以下持久化支持的 POJO:

public class Entity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "name", nullable = false)
    private String name;

    // SKIPPED

    @OneToOne(mappedBy = "entity", cascade = CascadeType.ALL)
    private EntityMetadata entityMetadata;

}   

以及此实体的一些集合:

List<Entity> entities

DTO 介绍如下:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ScreenDocumentDto { // dto
    private Long id;
    private String name;
    List<SomeLinkDto> someLinks;
}

和嵌套 DTO:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SomeLinkDto {
    private Long id;
    private ZonedDateTime someDate;
}

除了 someLinks 列表之外的所有字段我需要从实体映射:

dto.setId(entity.getId());
dto.setName(entity.getName());
// SKIPPED

我填充的嵌套列表如下:

List<SomeLinkDto> someLinks = new ArrayList<>(entities.size());
for (Entity entity  : entities) {
    someLinks.add(SomeLinkDto.builder().id(entity.getId())
        .someDate(entity.getEntityMetadata().getSomeDate()).build());
}

结果涉及大量手动转换:

@Mapper(componentModel = "spring")
public interface ScreenDocumentMapper extends BaseMapper<Entity, ScreenDocumentDto> {
    default ScreenDocumentDto toScreenDocumentDto(List<Entity> entities, Entity entity) {
        ScreenDocumentDto dto = new ScreenDocumentDto();
        List<SomeLinkDto> someLinks = new ArrayList<>(entities.size());
        for (Entity entity  : entities) {
            someLinks.add(SomeLinkDto.builder().id(entity.getId())
                .someDate(entity.getEntityMetadata().getSomeDate()).build());
        }

        dto.setId(entity.getId());
        dto.setName(entity.getName());
        ...
        dto.setSomeLinks(someLinks);

        return dto;
    }

    // SKIPPED

有没有办法通过使用 MapStruct 的特性来完成同样的工作?像这样:

@Mapper(componentModel = "spring")
public interface ScreenDocumentMapper extends BaseMapper<Entity, ScreenDocumentDto> {
    @Mappings({
        @Mapping(source = "...", target = "..."),
            ...
    })
    ScreenDocumentDto toDto(Entity entity);
}

您可以通过使用多个源参数来使用 MapStruct 实现自定义 toScreenDocumentDto 方法。

例如

@Mapper(componentModel = "spring")
public interface ScreenDocumentMapper extends BaseMapper<Entity, ScreenDocumentDto> {

    @Mapping(source = "entities", target = "someLinks")
    ScreenDocumentDto toDto(Entity entity, List<Entity> entities);

    @Mapping(source = "entityMetadata.someDate", target = "someDate")
    SomeLinkDto toLinkDto(Entity entity);
}

如果 Entity 具有 属性 的链接,您可以:

@Mapper(componentModel = "spring")
public interface ScreenDocumentMapper extends BaseMapper<Entity, ScreenDocumentDto> {

    @Mapping(source = "links", target = "someLinks")
    ScreenDocumentDto toDto(Entity entity);

    @Mapping(source = "entityMetadata.someDate", target = "someDate")
    SomeLinkDto toLinkDto(Entity entity);
}

在您的映射方法下添加以下注释。

@Mapping(target = "someLinks",expression = "java(Arrays.asList(new SomeLinkDTO(id,someDate)))"

并在映射器界面之上添加注释。

@Mapper(imports = {Arrays.class ,SomeLinkDTO.class})