How can I use another mapping from different class in mapstruct

Viewed 27355

I would like to mapping a model object to dto model. I already have mapper for one of the object. How can I reuse this mapper in another mapper which is in another class?

I have below as model

    @Getter
    @AllArgsConstructor
    @ToString
    public class History {

      @JsonProperty("identifier")
      private final Identifier identifier;

    @JsonProperty("submitTime")
    private final ZonedDateTime submitTime;

    @JsonProperty("method")
    private final String method;

    @JsonProperty("reason")
    private final String reason;

    @JsonProperty("dataList")
    private final List<Data> dataList;
   }

     @DynamoDBTable(tableName = "history")
     @Data
     @NoArgsConstructor
     public class HistoryDynamo {
        @DynamoDBRangeKey(attributeName = "submitTime")
        @DynamoDBTypeConverted(converter = ZonedDateTimeType.Converter.class)
        private ZonedDateTime submitTime;

        @DynamoDBAttribute(attributeName = "identifier")
        @NonNull
        private Identifier identifier;

        @DynamoDBAttribute(attributeName = "method")
        private String method;

         @DynamoDBAttribute(attributeName = "reason")
         private String reason;

         @DynamoDBAttribute(attributeName = "dataList")
         private List<Data> dataList;
     }

        @Data
        @DynamoDBDocument
        @NoArgsConstructor
        public class Identifier implements Serializable {
    
            @DynamoDBAttribute(attributeName = "number")
            private String number;
    
        @DynamoDBAttribute(attributeName = "cityCode")
        @NonNull
        private String cityCode;
    
        @DynamoDBAttribute(attributeName = "countryCode")
        @NonNull
        private String countryCode;
    
        @DynamoDBTypeConverted(converter = LocalDateType.Converter.class)
        private LocalDate mydate;
    }
    
         @Data
         @EqualsAndHashCode
         @NoArgsConstructor
         @RequiredArgsConstructor
         @JsonInclude(JsonInclude.Include.NON_NULL)
         public class Identifier implements Serializable {
    
        @NonNull
        @lombok.NonNull
        @NotNull
        private String number;
    
        @NonNull
        @lombok.NonNull
        @NotNull
        private City city;
    
        @NonNull
        @lombok.NonNull
        @NotNull
        private Country country;
    
        @JsonDeserialize(using = LocalDateDeserializer.class)
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'Z'")
        @DateTimeFormat(pattern = "yyyy-MM-dd'Z'")
        @NonNull
        @lombok.NonNull
        @NotNull
        private LocalDate mydate;
    }

And here is my mapping

    @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.WARN, injectionStrategy = InjectionStrategy.CONSTRUCTOR, nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL)
    public interface IdentifierMapper {
    
        IdentifierMapper MAPPER = Mappers.getMapper(IdentifierMapper.class);
    
    
        @Mappings({@Mapping(source = "identifier.number", target = "number"),
                   @Mapping(source = "identifier.city.code", target = "cityCode"),
                   @Mapping(source = "identifier.country.code", target = "countryCode"),
                   @Mapping(source = "identifier.mydate", target = "mydate")})
        @Named("toIdentifierDynamo")
        myproject.entity.dynamo.Identifier toIdentifierDynamo(myproject.model.Identifier identifier);
    }
    
    @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.WARN, injectionStrategy = InjectionStrategy.CONSTRUCTOR,
            nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL, uses = {IdentifierMapper.class})
    public interface HistoryMapper {
    
        HistoryMapper MAPPER = Mappers.getMapper(HistoryMapper.class);
    
        @Mappings({@Mapping(source = "identifier", target = "identifier", qualifiedByName = "toIdentifierDynamo"),
                  @Mapping(source = "method", target = "method"),
                  @Mapping(source = "reason", target = "reason"),
                  @Mapping(source = "timestamp", target = "timestamp")})
        HistoryDynamo toHistoryDynamo(History history);
    }

I would like to map History to HistoryDynamo and reuse IdentifierMapper to map one of the object in HistoryDynamo. How can I use toIdentifierDynamo in toHistoryDynamo?

2 Answers
  • First of all you don't have to create instance in Spring. You could just Autowire your Mapper.
  • Second of all you don't have to provide @Mapping annotation for each field if it has the same name. Mapstruct will do it for you.
  • Your issue could be done using uses parameter of MapStruct mapper HistoryMapper could have in @Mapper annotation parameter uses = IdentifierMapper.class. It will autowire IdentifierMapper into HistoryMapper. By default it will do via field. You could change it also in parameters: injectionStrategy = InjectionStrategy.CONSTRUCTOR and probably it will be enough as you have the same name of field (identifier) and MapStruct should realize that should be use IdentifierMapper

Using spring dependency can be injected easily as

private final HistoryMapper 
 historyMapper;

Also for fields with same name in target and source no need to use @Mapping, so in above case below mapper definition is enough to acheive desired result.

@Mapper(
    componentModel = "spring",
    injectionStrategy = InjectionStrategy.CONSTRUCTOR,
    uses = {IdentifierMapper.class})
public interface HistoryMapper {

  HistoryDynamo toHistoryDynamo(History history);

} 

Refer github sample here https://github.com/rakesh-singh-samples/map-struct-samples/tree/stack-question-60523230/src/sample/mapstruct/mapper

Related