Java MapStruct:Mapper 在映射单个元素之前清除目标集合

Java MapStruct: Mapper clears the target collection before it maps the individual elements

问题:我的映射器没有对单个元素使用我的自定义映射方法。


这是我的代码:

RelatedCardObject 实体

此实体与卡片实体相关。 CardMapper 使用 RelatedCardObjectMapper 映射 Set

@Entity
@Table(name = "related_card_object")
public class RelatedCardObject {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "related_card_object_sequence")
    @SequenceGenerator(name = "related_card_object_sequence", sequenceName = "related_card_object_seq")
    private Long id;

    @NotBlank
    @NotNull
    private String relatedCardObjectsScryfallId;

    @NotBlank
    @NotNull
    private String object;

    @NotBlank
    @NotNull
    private String name;

    @NotBlank
    @NotNull
    private String typeLine;

    @NotBlank
    @NotNull
        private String uri;
    
        @NotNull
        @ManyToMany(mappedBy = Card.ATTR_RELATEDCARDOBJECTS)
        private Set<Card> cards;

        // getters, setters & default constructor 
}

RelatedCardObject DTO

@JsonIgnoreProperties(ignoreUnknown = true)
public class RelatedCardObjectDto implements Serializable {

    @JsonIgnore
    private Long id;

    @JsonProperty("id")
    private String relatedCardObjectsScryfallId;

    @JsonProperty("object")
    private String object;

    @JsonProperty("name")
    private String name;

    @JsonProperty("type_line")
    private String typeLine;
    
        @JsonProperty("uri")
        private String uri;
    
        // getters, setters & default constructor
}

RelatedCardObjectMapper

@Mapper(componentModel = "spring")
public interface RelatedCardObjectMapper {

    Set<RelatedCardObject> updateRelatedCardObjectSetFromDto(Set<RelatedCardObjectDto> dtos, @MappingTarget Set<RelatedCardObject> entities);

    @Mapping(source = "dto.id", target = "entity.id", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
    RelatedCardObject relatedCardObjectFromDto(RelatedCardObjectDto dto, @MappingTarget RelatedCardObject entity);
}

生成了 RelatedCardObjectMapperImpl

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-11-01T11:58:42+0100",
    comments = "version: 1.4.1.Final, compiler: javac, environment: Java 14 (Oracle Corporation)"
)
@Component
public class RelatedCardObjectMapperImpl implements RelatedCardObjectMapper {

    @Override
    public Set<RelatedCardObject> updateRelatedCardObjectSetFromDto(Set<RelatedCardObjectDto> dtos, Set<RelatedCardObject> entities) {
        if ( dtos == null ) {
            return null;
        }

        entities.clear();
        for ( RelatedCardObjectDto relatedCardObjectDto : dtos ) {
            entities.add( relatedCardObjectDtoToRelatedCardObject( relatedCardObjectDto ) ); // here I want it to call my method  (`relatedCardObjectFromDto()`) 
        }

        return entities;
    }

    @Override
    public RelatedCardObject relatedCardObjectFromDto(RelatedCardObjectDto dto, RelatedCardObject entity) {
        if ( dto == null ) {
            return null;
        }

        if ( dto.getId() != null ) {
            entity.setId( dto.getId() );
        }
        entity.setRelatedCardObjectsScryfallId( dto.getRelatedCardObjectsScryfallId() );
        entity.setObject( dto.getObject() );
        entity.setName( dto.getName() );
        entity.setTypeLine( dto.getTypeLine() );
        entity.setUri( dto.getUri() );

        return entity;
    }

    protected RelatedCardObject relatedCardObjectDtoToRelatedCardObject(RelatedCardObjectDto relatedCardObjectDto) {
        if ( relatedCardObjectDto == null ) {
            return null;
        }

        RelatedCardObject relatedCardObject = new RelatedCardObject();

        relatedCardObject.setId( relatedCardObjectDto.getId() );
        relatedCardObject.setRelatedCardObjectsScryfallId( relatedCardObjectDto.getRelatedCardObjectsScryfallId() );
        relatedCardObject.setObject( relatedCardObjectDto.getObject() );
        relatedCardObject.setName( relatedCardObjectDto.getName() );
        relatedCardObject.setTypeLine( relatedCardObjectDto.getTypeLine() );
        relatedCardObject.setUri( relatedCardObjectDto.getUri() );

        return relatedCardObject;
    }
}

已从 CardMapper 正确调用生成的 MapperImpl 中的方法 updateRelatedCardObjectSetFromDto(),并且存在正确的 DTO 和实体列表。 但是在映射单个元素之前,方法 updateRelatedCardObjectSetFromDto() 清除实体集合。

它不使用我对单个实体的自定义映射 (relatedCardObjectFromDto()),而是使用生成的方法 (relatedCardObjectDtoToRelatedCardObject()) 来映射单个实体。


当您使用 Set 定义更新方法时,您实际上是在告诉 MapStruct 请用这个新集合中的元素更新我当前的集合。 MapStruct 无法知道集合中的哪个元素相同以及如何合并 2 个集合。

要实现您正在寻找的内容,您必须编写一些自定义代码。

@Mapper(componentModel = "spring")
public interface RelatedCardObjectMapper {

    default Set<RelatedCardObject> updateRelatedCardObjectSetFromDto(Set<RelatedCardObjectDto> dtos, @MappingTarget Set<RelatedCardObject> entities) {
        // Somehow find the matching object from dtos to update in entities and then invoke relatedCardObjectFromDto with it

    }

    @Mapping(source = "dto.id", target = "entity.id", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
    RelatedCardObject relatedCardObjectFromDto(RelatedCardObjectDto dto, @MappingTarget RelatedCardObject entity);
}