0
votes
  • I want to have a custom mapper for the elements in the collection. In my case a Set

The problem: My mapper does not use my custom mapping method for the individual elements.


This is my code:

RelatedCardObject Entity

This entity is related to the Card-Entity. The CardMapper uses the RelatedCardObjectMapper to map the 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);
}

Generated 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;
    }
}

The method updateRelatedCardObjectSetFromDto() in generated MapperImpl gets called correctly from the CardMapper and there is the correct list of DTOs and Entities. But before the individual element gets mapped, the method updateRelatedCardObjectSetFromDto() clears the entities collection.

It does not use my custom Mapping for the individual entities (relatedCardObjectFromDto()) instead it uses a generated method (relatedCardObjectDtoToRelatedCardObject()) to map the individual entities.


  • How can i make the mapper use my custom method to map the individual entities.
  • It should not clear the collection before the mapper maps the contained entities, because the entities-collection contains the entities from the database they contain the entity database id.
1

1 Answers

0
votes

When you are defining an update method with a Set you are actually telling MapStruct please update my current set with the elements from this new set. MapStruct can't know which element of the set is the same and how to merge 2 sets.

To achieve what you are looking for you'll have to write some custom code.

@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);
}