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. Несопоставленные свойства
Поскольку MapStruct работает во время компиляции, он может работать быстрее, чем инфраструктура динамического отображения. Он также может генерировать отчеты об ошибках, если сопоставления не завершены , то есть если сопоставлены не все целевые свойства:
Warning:(X,X) java: Unmapped target property: "propertyName".
Хотя это полезное предупреждение в случае аварии, мы можем предпочесть действовать по-другому, если поля намеренно отсутствуют.
Давайте рассмотрим это на примере сопоставления двух простых объектов:
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;
}
У нас есть уникальные поля в обоих классах, которые не должны быть заполнены во время сопоставления. Они есть:
комментарии
вDocumentDTO
автор
вDocumentDTO
время модификации
вдокументе
Если мы определим интерфейс картографа, это приведет к предупреждающим сообщениям во время сборки:
@Mapper
public interface DocumentMapper {
DocumentMapper INSTANCE = Mappers.getMapper(DocumentMapper.class);
DocumentDTO documentToDocumentDTO(Document entity);
Document documentDTOToDocument(DocumentDTO dto);
}
Поскольку мы не хотим сопоставлять эти поля, мы можем исключить их из сопоставления несколькими способами.
4. Игнорирование определенных полей
Чтобы пропустить несколько свойств в конкретном методе сопоставления, мы можем использовать свойство ignore
в аннотации @Mapping
:
@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);
}
Здесь мы указали имя поля в качестве цели
и установили для параметра ignore
значение true
, чтобы показать, что оно не требуется для сопоставления.
Однако этот метод не удобен для некоторых случаев. Нам может быть сложно его использовать, например, при использовании больших моделей с большим количеством полей.
5. Несопоставленная целевая политика
Чтобы сделать код более понятным и читаемым, мы можем указать несопоставленную целевую политику .
Для этого мы используем MapStruct unmappedTargetPolicy ,
чтобы обеспечить желаемое поведение, когда для сопоставления нет исходного поля:
ОШИБКА
: любое несопоставленное целевое свойство приведет к сбою сборки — это может помочь нам избежать случайного несопоставления полей.WARN
: (по умолчанию) предупреждающие сообщения во время сборкиIGNORE
: нет вывода или ошибок
Чтобы игнорировать несопоставленные свойства и не получать предупреждений о выводе, мы должны присвоить значение IGNORE
для unmappedTargetPolicy
. Есть несколько способов сделать это в зависимости от цели.
5.1. Установите политику для каждого картографа
Мы можем установить для unmappedTargetPolicy аннотацию
@Mapper .
В результате все его методы будут игнорировать несопоставленные свойства:
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface DocumentMapperUnmappedPolicy {
// mapper methods
}
5.2. Используйте общий MapperConfig
Мы можем игнорировать несопоставленные свойства в нескольких сопоставителях, установив `` unmappedTargetPolicy через @MapperConfig
,
чтобы совместно использовать настройку для нескольких сопоставителей.
Сначала создадим аннотированный интерфейс:
@MapperConfig(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface IgnoreUnmappedMapperConfig {
}
Затем мы применяем эту общую конфигурацию к картографу:
@Mapper(config = IgnoreUnmappedMapperConfig.class)
public interface DocumentMapperWithConfig {
// mapper methods
}
Следует отметить, что это простой пример, показывающий минимальное использование @MapperConfig,
что может показаться не намного лучше, чем установка политики для каждого картографа. Общая конфигурация становится очень полезной, когда необходимо стандартизировать несколько настроек для нескольких картографов.
5.3. Параметры конфигурации
Наконец, мы можем настроить параметры обработки аннотаций генератора кода MapStruct. При использовании Maven мы можем передавать параметры процессора с помощью параметраcompileArgs подключаемого
модуля процессора:
<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>
В этом примере мы игнорируем несопоставленные свойства во всем проекте.
6. Порядок старшинства
Мы рассмотрели несколько способов, которые могут помочь нам обрабатывать частичные сопоставления и полностью игнорировать несопоставленные свойства. Мы также видели, как применять их независимо друг от друга в картографе, но мы также можем их комбинировать.
Предположим, у нас есть большая кодовая база bean-компонентов и картографов с конфигурацией MapStruct по умолчанию. Мы не хотим разрешать частичное сопоставление, за исключением нескольких случаев. Мы могли бы легко добавить больше полей в bean-компонент или его сопоставленный аналог и получить частичное сопоставление, даже не заметив этого.
Таким образом, вероятно, было бы неплохо добавить глобальную настройку через конфигурацию Maven, чтобы сделать сборку неудачной в случае частичного сопоставления.
Теперь, чтобы разрешить несопоставленные свойства в некоторых наших преобразователях и переопределить глобальное поведение , мы можем комбинировать методы, учитывая порядок приоритета (от высшего к низшему):
- Игнорирование определенных полей на уровне метода сопоставления
- Политика на маппере
- Общий MapperConfig
- Глобальная конфигурация
7. Заключение
В этом руководстве мы рассмотрели, как настроить MapStruct для игнорирования несопоставленных свойств.
Во-первых, мы рассмотрели, что означают несопоставленные свойства для сопоставления. Затем мы увидели, как частичное отображение может быть разрешено без ошибок несколькими различными способами.
Наконец, мы научились комбинировать эти техники, не забывая об их приоритетности.
Как всегда, код из этого руководства доступен на GitHub .