1. 概述

MapStruct 是一个Java Bean 映射器。本文我们将学习如何在MapStruct中实现局部映射,忽略某些不需要的字段。

2. MapStruct基础用法

首先我们回顾下MapStruct的基础用法。

假设我们有两个Java Bean: Car 和 CarDTO 需要转换

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

首先我们需要定义一个mapper接口,MapStruct 在编译过程中会扫描 Java 注解,自动生成映射器的实现:

@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");
    
    // Car 转 CarDTO
    CarDTO carDto = CarMapper.INSTANCE.carToCarDTO(entity);

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

3. 未映射的属性

由于 MapStruct 在编译时运行,因此它比其他动态映射框架要更快。

同时,如果映射不完整,即有的源属性在目标属性中不存在时,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;
}

DocumentMapper定义:

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

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

我们不希望映射的字段:

  • DocumentDTO 中的 comments
  • DocumentDTO 中的 author
  • Document 中的 modificationTime

默认配置下MapStruct在编译时会提示 Unmapped Warning,下面我们讲解如何解决该问题

4. 忽略指定字段

方法一,通过 @Mapping 注解的 ignore 参数忽略指定字段:

@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);
}

该方法简单明了,但字段比较多的时候就显得繁琐难用了。

5. 配置映射策略

对于 unmapped 的字段,MapStruct 有3种错误级别:

  • ERROR: 错误,会直接导致构建失败
  • WARN: 警告(默认),不会失败但会打印警告
  • IGNORE: 忽略,不会有任何错误输出

所以,为了忽略警告,可以通过给 unmappedTargetPolicy 设置 IGNORE 实现

5.1. 每个Mapper设置单独的策略

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

5.2. 共享 MapperConfig

首先定义一个共享的配置接口

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

然后应用配置:

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

注意,这是一个最简单的@MapperConfig使用例子,似乎并不比在每个映射器上设置策略好多少。当配置项多了的时候,共享配置就变得非常有用。

5.3. 通过mvn配置

方法三,我们也可以直接在 mvn 插件中通过compilerArgs参数进行全局配置

<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. 优先顺序

本文我们介绍了4种方式用于解决部分映射导致的警告问题。根据实际场景可以独立使用,也可组合使用。

对于大型项目,一般会包含大量的 bean 和 mapper。通常情况下,我们不希望允许部分映射。 随着时间推移,往bean中新增字段时难免忘记同步而未被察觉到。

因此,通过 Maven 配置添加一个全局设置,以便在出现部分映射时让构建失败,可能是个不错的主意。

对于希望屏蔽的错误,我们可以覆盖全局行为。其优先级顺序如下(从高到低):

  • 在映射方法级别忽略特定字段
  • 映射器上的策略
  • 共享的 MapperConfig
  • 全局配置

7. 总结

本文我们介绍了几种解决 MapStruct 中 Unmapped target property 警告的方法。

最后,文中的示例代码可从GitHub上获取。


« 上一篇: 2019年Java的状态
» 下一篇: Java Weekly, 第301期