MapStruct — настраиваемое отображение целевого поля на основе 2 или более разных исходных объектов

#java #mapstruct

#java #mapstruct

Вопрос:

Я пытаюсь выяснить, как реализовать следующее сопоставление:

 class SuperComplexClass {
    Long value;
    String description;
}

class MapIntoMe {
    
    // Many other fields that is also mapped
    
    SuperComplexClass superComplexObject;
}

class MapFromMe {
    ComplexClassPart1 complexClassPart;
}

class AdditionalData {
    ComplexClassPart2 complexClassPart;
}


@Mapper
public interface SomeFancyMapper {

    @Mapping(target = "superComplexObject", source = "{mfm.complexPart, ad.complexPart}",
             qualifiedByName = "mapSuperComplexObject")
    MapIntoMe mapFromMeIntoMe(MapFromMe mfm, AdditionalData ad);
    

    @Named("mapSuperComplexObject")
    default SuperComplexClass mapSuperComplexObject(ComplexPart1 p1, ComplexPart2 p2) {
        SuperComplexClass superObject = new SuperComplexClass();
        //some logic that calculates and fills superObject]
        return superObject;
    }
}
  

И теперь, очевидно, выражение like source = "{mfm.complexPart, ad.complexPart}" не работает, но оно ясно показывает, чего я хотел бы достичь.

До сих пор я не смог найти ответ, если это возможно при таком подходе и без каких-либо уродливых обходных путей.

Есть идеи?

Ответ №1:

В настоящее время не поддерживается повторное использование методов сопоставления с более чем одним параметром. Вот почему что-то вроде выражения, которым вы поделились, не работает.

Однако вы можете использовать expression , @AfterMapping или @Context (в случае, если вам не нужно использовать AdditionalData для другого сопоставления) для достижения того, что вам нужно.

Использование выражения

 @Mapper
public interface SomeFancyMapper {

    @Mapping(target = "superComplexObject", expression = "java(mapSuperComplexObject(mfm.getComplexPart(), ad.getComplexPart()))")
    MapIntoMe mapFromMeIntoMe(MapFromMe mfm, AdditionalData ad);
    

    default SuperComplexClass mapSuperComplexObject(ComplexPart1 p1, ComplexPart2 p2) {
        SuperComplexClass superObject = new SuperComplexClass();
        //some logic that calculates and fills superObject]
        return superObject;
    }
}
  

Использование @AfterMapping

 @Mapper
public interface SomeFancyMapper {

    @Mapping(target = "superComplexObject", ignore = true)
    MapIntoMe mapFromMeIntoMe(MapFromMe mfm, AdditionalData ad);
    
    @AfterMapping
    default void mapSuperComplexObject(@MappingTarget MapIntoMe target, MapFromMe mfm, AdditionalData ad) {

        SuperComplexClass superObject = new SuperComplexClass();
        //some logic that calculates and fills superObject]
        return superObject;
    }
}
  

Использование @Context

 @Mapper
public interface SomeFancyMapper {

    @Mapping(target = "superComplexObject", source = "complexPart",
             qualifiedByName = "mapSuperComplexObject")
    MapIntoMe mapFromMeIntoMe(MapFromMe mfm, @Context AdditionalData ad);
    

    @Named("mapSuperComplexObject")
    default SuperComplexClass mapSuperComplexObject(ComplexPart1 p1, @Context AdditionalData ad) {
        SuperComplexClass superObject = new SuperComplexClass();
        //some logic that calculates and fills superObject]
        return superObject;
    }
}
  

Имейте в виду, что при использовании @Context параметра, аннотированного этой аннотацией, его нельзя использовать Mapping#target . Это дополнительный контекст, который может быть передан другим методам отображения или методам жизненного цикла.