«1. Обзор

В Java-приложениях может потребоваться копирование значений из одного типа Java-бина в другой. Чтобы избежать длинного, подверженного ошибкам кода, мы можем использовать средство сопоставления компонентов, такое как MapStruct.

Хотя сопоставление идентичных полей с одинаковыми именами полей очень просто, мы часто сталкиваемся с несоответствующими bean-компонентами. В этом руководстве мы рассмотрим, как MapStruct обрабатывает частичное сопоставление.

2. Отображение

MapStruct — это процессор аннотаций Java. Поэтому все, что нам нужно сделать, это определить интерфейс картографа и объявить методы сопоставления. MapStruct создаст реализацию этого интерфейса во время компиляции.

Для простоты давайте начнем с двух классов с одинаковыми именами полей:

public class CarDTO {
    private int id;
    private String name;
}
public class Car {
    private int id;
    private String name;
}

@Mapper
public interface CarMapper {
    CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
    CarDTO carToCarDTO(Car car);
}

Затем давайте создадим интерфейс преобразователя:

@Test
public void givenCarEntitytoCar_whenMaps_thenCorrect() {
    Car entity = new Car();
    entity.setId(1);
    entity.setName("Toyota");

    CarDTO carDto = CarMapper.INSTANCE.carToCarDTO(entity);

    assertThat(carDto.getId()).isEqualTo(entity.getId());
    assertThat(carDto.getName()).isEqualTo(entity.getName());
}

Наконец, давайте протестируем наш преобразователь:

3. Несопоставленные свойства

Warning:(X,X) java: Unmapped target property: "propertyName".

Поскольку MapStruct работает во время компиляции, он может быть быстрее, чем структура динамического отображения. Он также может генерировать отчеты об ошибках, если сопоставления не завершены, то есть если сопоставлены не все целевые свойства:

Хотя это полезное предупреждение в случае аварии, мы можем предпочесть справиться с ситуацией по-другому, если поля отсутствуют намеренно.

public class DocumentDTO {
    private int id;
    private String title;
    private String text;
    private List<String> comments;
    private String author;
}
public class Document {
    private int id;
    private String title;
    private String text;
    private Date modificationTime;
}

Давайте рассмотрим это на примере сопоставления двух простых объектов:

У нас есть уникальные поля в обоих классах, которые не должны быть заполнены во время сопоставления. Это:

@Mapper
public interface DocumentMapper {
    DocumentMapper INSTANCE = Mappers.getMapper(DocumentMapper.class);

    DocumentDTO documentToDocumentDTO(Document entity);
    Document documentDTOToDocument(DocumentDTO dto);
}

комментарии в DocumentDTO автор в DocumentDTO модификацияВремя в документе

Если мы определим интерфейс сопоставления, это приведет к предупреждающим сообщениям во время сборки:

Поскольку мы не хотим сопоставлять эти поля , мы можем исключить их из сопоставления несколькими способами.

@Mapper
public interface DocumentMapperMappingIgnore {

    DocumentMapperMappingIgnore INSTANCE =
      Mappers.getMapper(DocumentMapperMappingIgnore.class);

    @Mapping(target = "comments", ignore = true)
    @Mapping(target = "author", ignore = true)
    DocumentDTO documentToDocumentDTO(Document entity);

    @Mapping(target = "modificationTime", ignore = true)
    Document documentDTOToDocument(DocumentDTO dto);
}

4. Игнорирование определенных полей

Чтобы пропустить несколько свойств в конкретном методе сопоставления, мы можем использовать свойство ignore в аннотации @Mapping:

Здесь мы указали имя поля в качестве target и установите для ignore значение true, чтобы показать, что это не требуется для сопоставления.

To make things clearer and the code more readable, we can specify the unmapped target policy.

To do this, we use the MapStruct unmappedTargetPolicy to provide our desired behavior when there is no source field for the mapping:

    Однако в некоторых случаях этот метод неудобен. Нам может быть сложно его использовать, например, при использовании больших моделей с большим количеством полей.

5. Несопоставленная целевая политика

ОШИБКА: любое несопоставленное целевое свойство приведет к сбою сборки — это может помочь нам избежать случайного несопоставления полей. ПРЕДУПРЕЖДЕНИЕ: (по умолчанию) предупреждающие сообщения во время сборки ИГНОРИРОВАТЬ: никаких выходных данных или ошибок ~~ ~ Чтобы игнорировать несопоставленные свойства и не получать предупреждений на выходе, мы должны присвоить значение IGNORE для unmappedTargetPolicy. Есть несколько способов сделать это в зависимости от цели.

5.1. Установите политику для каждого Mapper

@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface DocumentMapperUnmappedPolicy {
    // mapper methods
}

Мы можем установить unmappedTargetPolicy на аннотацию @Mapper. В результате все его методы будут игнорировать неотображенные свойства:

5.2. Используйте Shared MapperConfig

Мы можем игнорировать несопоставленные свойства в нескольких преобразователях, настроив unmappedTargetPolicy через @MapperConfig для совместного использования параметра несколькими преобразователями.

@MapperConfig(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface IgnoreUnmappedMapperConfig {
}

Сначала мы создаем аннотированный интерфейс:

@Mapper(config = IgnoreUnmappedMapperConfig.class)
public interface DocumentMapperWithConfig { 
    // mapper methods 
}

Затем мы применяем эту общую конфигурацию к преобразователю:

Следует отметить, что это простой пример, показывающий минимальное использование @MapperConfig, которое может показаться не намного лучше, чем установка политики для каждого картографа. Общая конфигурация становится очень полезной, когда необходимо стандартизировать несколько настроек для нескольких картографов.

5.3. Параметры конфигурации

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <source>${maven.compiler.source}</source>
                <target>${maven.compiler.target}</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
                <compilerArgs>
                    <compilerArg>
                        -Amapstruct.unmappedTargetPolicy=IGNORE
                    </compilerArg>
                </compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

Наконец, мы можем настроить параметры обработки аннотаций генератора кода MapStruct. При использовании Maven мы можем передавать параметры процессора с помощью параметраcompileArgs подключаемого модуля процессора:

В этом примере мы игнорируем несопоставленные свойства во всем проекте.

6. Порядок приоритета

Мы рассмотрели несколько способов, которые могут помочь нам обрабатывать частичные отображения и полностью игнорировать неотображенные свойства. Мы также видели, как применять их независимо друг от друга в картографе, но мы также можем их комбинировать.

«Предположим, у нас есть большая кодовая база bean-компонентов и картографов с конфигурацией MapStruct по умолчанию. Мы не хотим разрешать частичное сопоставление, за исключением нескольких случаев. Мы могли бы легко добавить больше полей в bean-компонент или его сопоставленный аналог и получить частичное сопоставление, даже не заметив этого.

Так что, вероятно, было бы неплохо добавить глобальную настройку через конфигурацию Maven, чтобы сделать сборку неудачной в случае частичного сопоставления.

    Теперь, чтобы разрешить несопоставленные свойства в некоторых наших преобразователях и переопределить глобальное поведение, мы можем комбинировать методы, учитывая порядок приоритета (от высшего к низшему):

Игнорирование определенных полей в mapper method-level Политика для картографа Общий MapperConfig Глобальная конфигурация

7. Заключение

В этом руководстве мы рассмотрели, как настроить MapStruct для игнорирования несопоставленных свойств.

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

Наконец-то мы научились комбинировать эти приемы, не забывая об их приоритете.