MapStruct使用说明

Posted DRY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MapStruct使用说明相关的知识,希望对你有一定的参考价值。

1、简介

MapStruct是一个Java注释处理器,用于生成类型安全的bean映射类。
您要做的就是定义一个映射器接口,该接口声明任何必需的映射方法。在编译期间,MapStruct将生成此接口的实现。此实现使用简单的Java方法调用在源对象和目标对象之间进行映射,即没有反射或类似内容。
与手动编写映射代码相比,MapStruct通过生成繁琐且易于出错的代码来节省时间。遵循配置方法上的约定,MapStruct使用合理的默认值,但在配置或实现特殊行为时不加理会。

2、构建

2.1、Apache Maven

在 pom.xml 文件中,加入如下配置

...
<properties>
    <org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
</properties>
...
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>$org.mapstruct.version</version>
    </dependency>
</dependencies>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>$org.mapstruct.version</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
...

3、定义映射器

3.1、基本映射器

如果目标对象与源对象属性不一致,则需要明确指定属性名

@Mapper
public interface CarMapper 

    @Mapping(target = "manufacturer", source = "make")
    @Mapping(target = "seatCount", source = "numberOfSeats")
    CarDto carToCarDto(Car car);

    @Mapping(target = "fullName", source = "name")
    PersonDto personToPersonDto(Person person);

3.2、表达式

指定目标对象使用指定表达式赋值

@Mapping(target = "creationDate", expression = "java(new java.util.Date())")

3.3、自定义方法

如果复制对象内部有引用类型的成员,可自定义方法,完成引用类型之间的对应拷贝。

@Mapper
public interface CarMapper 

    @Mapping(...)
    ...
    CarDto carToCarDto(Car car);

    default PersonDto personToPersonDto(Person person) 
        //hand-written mapping logic
    

3.4、多个源对象

3.4.1、源数据来源多个对象

@Mapper
public interface AddressMapper 

    @Mapping(target = "description", source = "person.description")
    @Mapping(target = "houseNumber", source = "address.houseNo")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);

3.4.2、源数据来源普通参数

@Mapper
public interface AddressMapper 

    @Mapping(target = "description", source = "person.description")
    @Mapping(target = "houseNumber", source = "hn")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn);


3.5、嵌套对象属性

如果复制对象内部有相同的引用类型,指定成员属性名,可直接复制。如果不同,则需要明确指定成员变量的某个属性,对应另一个对象的某个成员属性的某个属性。

@Mapper
 public interface CustomerMapper 

     @Mapping( target = "name", source = "record.name" )
     @Mapping( target = ".", source = "record" )
     @Mapping( target = ".", source = "account" )
     Customer customerDtoToCustomer(CustomerDto customerDto);
 

3.6、更新bean实例

通过 @MappingTarget 注解,更新实例。

@Mapper
public interface CarMapper 

    void updateCarFromDto(CarDto carDto, @MappingTarget Car car);


3.7、public访问权限修饰符修饰的成员

public class Customer 

    private Long id;
    private String name;

    //getters and setter omitted for brevity


public class CustomerDto 

    public Long id;
    public String customerName;


@Mapper
public interface CustomerMapper 

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

    @Mapping(target = "name", source = "customerName")
    Customer toCustomer(CustomerDto customerDto);

    @InheritInverseConfiguration
    CustomerDto fromCustomer(Customer customer);

3.8、生成器

略。

3.9、构造器

MapStruct 支持使用构造函数来映射目标类型。执行映射时,MapStruct 会检查所映射的类型是否有构建器。如果没有构建器,则 MapStruct 会查找单个可访问的构造函数。当有多个构造函数时,执行以下操作来选择应该使用的构造函数:

  • 如果构造函数使用名为 @Default 的注释进行批注,则将使用该批注。
  • 如果存在单个公共构造函数,则将使用该构造函数构造对象,而忽略其他非公共构造函数。
  • 如果存在无参数构造函数,则它将用于构造对象,其他构造函数将被忽略。
  • 如果有多个符合条件的构造函数,则由于构造函数不明确,将出现编译错误。为了打破歧义,可以使用名为 @Default 的注释

3.10、映射Map到Bean

指定Map的key映射到对应Bean的属性。

public class Customer 

    private Long id;
    private String name;

    //getters and setter omitted for brevity


@Mapper
public interface CustomerMapper 

    // 此时map对象中包括键customerName
    @Mapping(target = "name", source = "customerName")
    Customer toCustomer(Map<String, String> map);


4、获取映射器示例

4.1、通过映射工厂获取实例

CarMapper mapper = Mappers.getMapper( CarMapper.class );

5、数据类型转化

5.1、隐式类型转化

8种基本类型与其包装类,可以自动转化。
8种基本类型与String,可以自动转化。

5.1.1、数字格式化

@Mapper
public interface CarMapper 

    @Mapping(source = "price", numberFormat = "$#.00")
    CarDto carToCarDto(Car car);

    @IterableMapping(numberFormat = "$#.00")
    List<String> prices(List<Integer> prices);

5.1.2、日期格式化

@Mapper
public interface CarMapper 

    @Mapping(source = "manufacturingDate", dateFormat = "dd.MM.yyyy")
    CarDto carToCarDto(Car car);

    @IterableMapping(dateFormat = "dd.MM.yyyy")
    List<String> stringListToDateList(List<Date> dates);

5.2、引用类型映射

明确指定属性即可完成复制。

5.3、嵌套类型映射

通过明确指定的属性值,完成复制。

5.4、自定义映射方法

略。

5.5、调用其他映射器

定义公用映射器

  public class DateMapper 

      public String asString(Date date) 
          return date != null ? new SimpleDateFormat( "yyyy-MM-dd" ).format( date ) : null;
      

      public Date asDate(String date) 
          try 
              return date != null ? new SimpleDateFormat( "yyyy-MM-dd" ).parse( date ) : null;
           catch ( ParseException e ) 
              throw new RuntimeException( e );
          
      
  

使用

  @Mapper(uses=DateMapper.class)
  public interface CarMapper 

      CarDto carToCarDto(Car car);
  

5.6、将映射目标类型传递给自定义映射器

略。

5.7、将上下文或状态对象传递给自定义方法

5.8、映射方法解析

使用 @Mapper#uses() 可重用映射器

5.9、限定符

使用限定符指定的类生成值

5.10、限定符结合默认值

使用限定符指定生成默认值方法

  @Mapper
  public interface MovieMapper 

       @Mapping( target = "category", qualifiedByName = "CategoryToString", defaultValue = "DEFAULT" )
       GermanRelease toGerman( OriginalRelease movies );

       @Named("CategoryToString")
       default String defaultValueForQualifier(Category cat) 
           // some mapping logic
       
  

此时 category 为空时,将使用 defaultValueForQualifier 方法生成默认值。

6、映射集合

7、映射流

8、映射值

9、对象工厂

10、高级映射配置

11、映射配置重用

12、自定义映射

13、SPI

14、第三方集成

MapStruct - 找不到实现

【中文标题】MapStruct - 找不到实现【英文标题】:MapStruct - Cannot find implementation 【发布时间】:2020-08-25 20:40:09 【问题描述】:

使用最新的 Springboot 和 MapStruct 版本并使用 Maven 构建,我正在尝试实现 the official MapStruct site 中给出的“从这里开始”示例

我的代码更简单:

pom.xml

<org.mapstruct.version>1.3.1.Final</org.mapstruct.version>

(...)

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>$org.mapstruct.version</version>
</dependency>

(...)

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <source>1.8</source>
            <target>1.8</target>
            <annotationProcessorPaths>
                <path>
                    <groupId>org.mapstruct</groupId>
                    <artifactId>mapstruct-processor</artifactId>
                    <version>$org.mapstruct.version</version>
                </path>
            </annotationProcessorPaths>
        </configuration>
    </plugin>

汽车.java

public class Car 

    private String model;

    // Constructors, setters and getters...


CarDto.java

public class CarDto 

    private String theModel;

    // Constructors, setters and getters...

CarMapper.java 接口

@Mapper
public interface CarMapper 

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

    @Mapping(source = "model", target = "theModel")
    CarDto carToCarDto(Car car);

主要应用

@SpringBootApplication
public class MappertestApplication 

    public static void main(String[] args) 
        SpringApplication.run(MappertestApplication.class, args);

        Car c = new Car("Volkswagen");

        CarDto cdto = CarMapper.INSTANCE.carToCarDto(c);

    


所有代码都在这个公共仓库中:https://github.com/pgbonino/mappertest

运行时出现这个错误:

Exception in thread "main" java.lang.ExceptionInInitializerError
    at com.gallelloit.mappertest.MappertestApplication.main(MappertestApplication.java:14)
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: Cannot find implementation for com.gallelloit.mappertest.CarMapper
    at org.mapstruct.factory.Mappers.getMapper(Mappers.java:61)
    at com.gallelloit.mappertest.CarMapper.<clinit>(CarMapper.java:10)
    ... 1 more
Caused by: java.lang.ClassNotFoundException: Cannot find implementation for com.gallelloit.mappertest.CarMapper
    at org.mapstruct.factory.Mappers.getMapper(Mappers.java:75)
    at org.mapstruct.factory.Mappers.getMapper(Mappers.java:58)
    ... 2 more

我在官方 MapStruct 项目中找到了this issue,它似乎描述了同样的问题。但是,在这种情况下,正在执行一些自定义配置(实现的自定义名称)。在我的情况下,一切都保留为默认值。

有什么想法吗?

【问题讨论】:

你找到解决办法了吗? 【参考方案1】:

虽然我的情况与您的情况不同,但它确实导致了相同的错误 - 因此我发布此答案是为了帮助其他与我犯相同错误并最终在这里寻找解决方案的人。

我正在导入 maven 依赖项:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>$org.mapstruct.version</version>
</dependency>

但是忘记在maven编译插件中添加注解处理器路径:

    <annotationProcessorPaths>
        <path>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>$org.mapstruct.version</version>
        </path>
    </annotationProcessorPaths>

【讨论】:

【参考方案2】:

我添加了 mapstruct 处理器依赖项,它对我有用

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.3.1.Final</version>
</dependency>

【讨论】:

【参考方案3】:

您需要确保您的 IDE 已正确配置为调用注释处理器。看看IDE Setup。

查看您提供的项目,代码甚至不应该编译。 MapStruct 处理器将发出编译错误,原因是:

CarDto 中没有默认构造函数 Car 中不存在属性model(只有marcaCarDto 中不存在属性theModel(只有laMarca

【讨论】:

感谢您的回答。在昨天的某个时刻,我推动了一些更改,只是为了将 marca 翻译成模型(西班牙语到英语)。但我不认为因为这个原因存在编译错误。没有默认构造函数。无论如何,我会添加它。我最后做的是给出一个 Spring 策略,将 Mapper 配置为上下文中的 bean 并将其注入客户端。我想知道当我提出一个对我来说不再有意义的问题时,我在 *** 中会做什么。无论如何,非常感谢您的回答和帮助:) 您的回答没有任何帮助,因为它没有给出任何适当的问题原因。错误指出Cannot find implementation,据我推测,这意味着该接口不是由 MapStruct 实现的。为什么 MapStruct 提供了正确的注解后仍然没有实现提供的接口? @Chetan,答案对用户没有帮助,因为还有另一个问题。但是,该问题通常在注释处理器未运行时出现,即 IDE 未正确配置。 我单击了您为 IDE 设置提供的链接。我遵循了 Eclipse IDE 所需的所有步骤。然而它并没有帮助我。我仍然收到声明 Cannot find implementation 的错误消息。【参考方案4】:

当我在我的项目上运行给定的命令时,我的问题得到了解决 -

mvn clean install

然后我注意到正在生成实现文件。我猜想生成实现需要执行 maven 命令。

【讨论】:

【参考方案5】:

尝试在 Eclipse IDE 中配置 MapStruct Eclipse 插件 来解决问题。

【讨论】:

【参考方案6】:

使用@Mapper(componentModel = "spring")是正确的

在你的 Mapper 类上,这样你就可以贪得无厌地使用注入,像这样:

@Autowired
private CarMapper carMapper;

为了检查一切是否正常,你可以在编译后检查你的映射器实现。它应该有@component。像这样

@Component
public class CarMapperImpl implements CarMapper 

【讨论】:

【参考方案7】:

我正在重现完全相同的错误,因为我忘记将@Mapper 添加到映射器界面

@Mapper // <-- missing
public interface MyMapper 

这是一个微不足道的错误,但很容易错过

【讨论】:

以上是关于MapStruct使用说明的主要内容,如果未能解决你的问题,请参考以下文章

MapStruct类型之间映射的实现

MapStruct类型之间映射的实现

MapStruct - 找不到实现

Mapstruct使用笔记

MapStruct 映射工具

Mapstruct官方参考文档中的一些使用规则