How can I use another mapping from different class in mapstruct
Asked Answered
A

2

14

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?

Almazan answered 4/3, 2020 at 9:51 Comment(4)
Which version of lombok and mapstruct are you using?Caron
mapstruct 1.3.0.Final lombok 1.18.8Almazan
It looks like there's a mistake in your class HistoryDynamo... the attribute identifier precedes its type, which is HistoryDynamo instead of... myproject.entity.dynamo.Identifier?Caron
Thanks. It was a typo. I have fixed the questionAlmazan
R
29
  • 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
Remediable answered 4/3, 2020 at 13:1 Comment(2)
Could you add some code examples here to make this answer clearer?Besot
Hmm, I am trying to implement the as stated above and in my implementation. I do see the mapper being created in the implementation on the main mapper but it lacks the Autowired annotation. Am I missing something how to get that annotation on the implementation mapper becuase it is causing NPE in the testsDawdle
G
12

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

Guide answered 8/2, 2022 at 17:45 Comment(2)
I tried the above example in my code but got an error when trying to run the app: HistoryMapperImpl required a bean of type 'IdentifierMapper' that could not be found. Am I missing something?Slivovitz
Ok I got it, my IdentifierMapper was missing the @Mapper(componentModel = "spring") configuration.Slivovitz

© 2022 - 2024 — McMap. All rights reserved.