Quick Guide to MapStruct – MapStruct快速指南

最后修改: 2016年 10月 18日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll explore the use of MapStruct, which is, simply put, a Java Bean mapper.

在本教程中,我们将探讨MapStruct的使用,简单地说,它是一个Java Bean映射器。

This API contains functions that automatically map between two Java Beans. With MapStruct, we only need to create the interface, and the library will automatically create a concrete implementation during compile time.

这个API包含了在两个Java Beans之间自动映射的函数。有了MapStruct,我们只需要创建接口,库就会在编译时自动创建一个具体的实现。

2. MapStruct and Transfer Object Pattern

2.MapStruct和转移对象模式

For most applications, you’ll notice a lot of boilerplate code converting POJOs to other POJOs.

对于大多数应用程序,你会注意到很多将POJO转换为其他POJO的模板代码。

For example, a common type of conversion happens between persistence-backed entities and DTOs that go out to the client-side.

例如,一种常见的转换类型发生在由持久性支持的实体和到客户端的DTOs之间。

So, that is the problem that MapStruct solves: Manually creating bean mappers is time-consuming. But the library can generate bean mapper classes automatically.

所以,这就是MapStruct解决的问题。手动创建Bean mappers是很耗时的。但是这个库可以自动生成Bean mapper类

3. Maven

3.Maven

Let’s add the below dependency into our Maven pom.xml:

让我们在Maven的pom.xml中添加以下依赖。

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.4.2.Final</version> 
</dependency>

The latest stable release of MapStruct and its processor are both available from the Maven Central Repository.

MapStruct及其处理器的最新稳定版都可以从Maven中央仓库获得。

Let’s also add the annotationProcessorPaths section to the configuration part of the maven-compiler-plugin plugin.

我们也把annotationProcessorPaths部分添加到maven-compiler-plugin插件的配置部分。

The mapstruct-processor is used to generate the mapper implementation during the build:

mapstruct-processor用于在构建期间生成映射器实现。

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

4. Basic Mapping

4.基本制图

4.1. Creating a POJO

4.1.创建一个POJO

Let’s first create a simple Java POJO:

让我们首先创建一个简单的Java POJO。

public class SimpleSource {
    private String name;
    private String description;
    // getters and setters
}
 
public class SimpleDestination {
    private String name;
    private String description;
    // getters and setters
}

4.2. The Mapper Interface

4.2.映射器接口

@Mapper
public interface SimpleSourceDestinationMapper {
    SimpleDestination sourceToDestination(SimpleSource source);
    SimpleSource destinationToSource(SimpleDestination destination);
}

Notice we did not create an implementation class for our SimpleSourceDestinationMapper — because MapStruct creates it for us.

注意我们没有为我们的SimpleSourceDestinationMapper创建一个实现类–因为MapStruct为我们创建了它。

4.3. The New Mapper

4.3.新的绘图仪

We can trigger the MapStruct processing by executing an mvn clean install.

我们可以通过执行mvn clean install来触发MapStruct处理。

This will generate the implementation class under /target/generated-sources/annotations/.

这将在/target/generated-sources/annotations/下生成实现类。

Here is the class that MapStruct auto-creates for us:

下面是MapStruct为我们自动创建的类。

public class SimpleSourceDestinationMapperImpl
  implements SimpleSourceDestinationMapper {
    @Override
    public SimpleDestination sourceToDestination(SimpleSource source) {
        if ( source == null ) {
            return null;
        }
        SimpleDestination simpleDestination = new SimpleDestination();
        simpleDestination.setName( source.getName() );
        simpleDestination.setDescription( source.getDescription() );
        return simpleDestination;
    }
    @Override
    public SimpleSource destinationToSource(SimpleDestination destination){
        if ( destination == null ) {
            return null;
        }
        SimpleSource simpleSource = new SimpleSource();
        simpleSource.setName( destination.getName() );
        simpleSource.setDescription( destination.getDescription() );
        return simpleSource;
    }
}

4.4. Test Case

4.4.测试案例

Finally, with everything generated, let’s write a test case showing that values in SimpleSource match values in SimpleDestination:

最后,在一切都生成后,让我们写一个测试案例,显示SimpleSource中的值与SimpleDestination中的值相匹配。

public class SimpleSourceDestinationMapperIntegrationTest {
    private SimpleSourceDestinationMapper mapper
      = Mappers.getMapper(SimpleSourceDestinationMapper.class);
    @Test
    public void givenSourceToDestination_whenMaps_thenCorrect() {
        SimpleSource simpleSource = new SimpleSource();
        simpleSource.setName("SourceName");
        simpleSource.setDescription("SourceDescription");
        SimpleDestination destination = mapper.sourceToDestination(simpleSource);
 
        assertEquals(simpleSource.getName(), destination.getName());
        assertEquals(simpleSource.getDescription(), 
          destination.getDescription());
    }
    @Test
    public void givenDestinationToSource_whenMaps_thenCorrect() {
        SimpleDestination destination = new SimpleDestination();
        destination.setName("DestinationName");
        destination.setDescription("DestinationDescription");
        SimpleSource source = mapper.destinationToSource(destination);
        assertEquals(destination.getName(), source.getName());
        assertEquals(destination.getDescription(),
          source.getDescription());
    }
}

5. Mapping With Dependency Injection

5.依赖注入的映射

Next, let’s obtain an instance of a mapper in MapStruct by merely calling Mappers.getMapper(YourClass.class).

接下来,让我们仅仅通过调用Mappers.getMapper(YourClass.class)来获得MapStruct中的一个映射器实例。

Of course, that’s a very manual way of getting the instance. However, a much better alternative is to inject the mapper directly where we need it (if our project uses any Dependency Injection solution).

当然,这是一种非常手动的获取实例的方式。然而,一个更好的选择是在我们需要的地方直接注入映射器(如果我们的项目使用任何依赖注入解决方案)。

Luckily, MapStruct has solid support for both Spring and CDI (Contexts and Dependency Injection).

幸运的是,MapStruct对Spring和CDI上下文和依赖注入)都有坚实的支持。

To use Spring IoC in our mapper, we need to add the componentModel attribute to @Mapper with the value spring, and for CDI, it would be cdi.

要在我们的映射器中使用Spring IoC,我们需要在@Mapper中添加componentModel属性,其值为spring,对于CDI,它将是cdi

5.1. Modify the Mapper

5.1.修改映射器

Add the following code to SimpleSourceDestinationMapper:

SimpleSourceDestinationMapper中添加以下代码。

@Mapper(componentModel = "spring")
public interface SimpleSourceDestinationMapper

5.2. Inject Spring Components into the Mapper

5.2.将Spring组件注入到映射器中

Sometimes, we’ll need to utilize other Spring components inside our mapping logic. In this case, we have to use an abstract class instead of an interface:

有时,我们需要在映射逻辑中利用其他Spring组件。在这种情况下,我们必须使用一个抽象类而不是一个接口

@Mapper(componentModel = "spring")
public abstract class SimpleDestinationMapperUsingInjectedService

Then, we can easily inject the desired component using a well-known @Autowired annotation and use it in our code:

然后,我们可以使用著名的@Autowired注解轻松注入所需的组件,并在我们的代码中使用它。

@Mapper(componentModel = "spring")
public abstract class SimpleDestinationMapperUsingInjectedService {

    @Autowired
    protected SimpleService simpleService;

    @Mapping(target = "name", expression = "java(simpleService.enrichName(source.getName()))")
    public abstract SimpleDestination sourceToDestination(SimpleSource source);
}

We must remember not to make the injected bean private! This is because MapStruct has to access the object in the generated implementation class.

我们必须记住不要把注入的bean变成私有的!这是因为MapStruct必须访问生成的实现类中的对象。

6. Mapping Fields With Different Field Names

6.用不同的字段名映射字段

From our previous example, MapStruct was able to map our beans automatically because they have the same field names. So, what if a bean we are about to map has a different field name?

从我们之前的例子来看,MapStruct能够自动映射我们的Bean,因为它们有相同的字段名。那么,如果我们要映射的Bean有一个不同的字段名呢?

In this example, we will be creating a new bean called Employee and EmployeeDTO.

在这个例子中,我们将创建一个名为EmployeeEmployeeDTO的新bean。

6.1. New POJOs

6.1.新的POJO

public class EmployeeDTO {
    private int employeeId;
    private String employeeName;
    // getters and setters
}
public class Employee {
    private int id;
    private String name;
    // getters and setters
}

6.2. The Mapper Interface

6.2.映射器接口

When mapping different field names, we will need to configure its source field to its target field and to do that, we will need to add @Mapping annotation for each field.

当映射不同的字段名时,我们需要将其源字段配置到目标字段,为此,我们需要为每个字段添加@Mapping注解。

In MapStruct, we can also use dot notation to define a member of a bean:

在MapStruct中,我们也可以使用点符号来定义Bean的一个成员。

@Mapper
public interface EmployeeMapper {
    
    @Mapping(target="employeeId", source="entity.id")
    @Mapping(target="employeeName", source="entity.name")
    EmployeeDTO employeeToEmployeeDTO(Employee entity);

    @Mapping(target="id", source="dto.employeeId")
    @Mapping(target="name", source="dto.employeeName")
    Employee employeeDTOtoEmployee(EmployeeDTO dto);
}

6.3. Test Case

6.3.测试案例

Again, we need to test that both source and destination object values match:

同样,我们需要测试源和目的对象的值是否匹配。

@Test
public void givenEmployeeDTOwithDiffNametoEmployee_whenMaps_thenCorrect() {
    EmployeeDTO dto = new EmployeeDTO();
    dto.setEmployeeId(1);
    dto.setEmployeeName("John");

    Employee entity = mapper.employeeDTOtoEmployee(dto);

    assertEquals(dto.getEmployeeId(), entity.getId());
    assertEquals(dto.getEmployeeName(), entity.getName());
}

More test cases can be found in the GitHub project.

更多的测试案例可以在GitHub项目中找到。

7. Mapping Beans With Child Beans

7.用孩子的豆子绘制豆子图

Next, we’ll show how to map a bean with references to other beans.

接下来,我们将展示如何用对其他Bean的引用来映射一个Bean。

7.1. Modify the POJO

7.1.修改POJO

Let’s add a new bean reference to the Employee object:

让我们为Employee对象添加一个新的bean引用。

public class EmployeeDTO {
    private int employeeId;
    private String employeeName;
    private DivisionDTO division;
    // getters and setters omitted
}
public class Employee {
    private int id;
    private String name;
    private Division division;
    // getters and setters omitted
}
public class Division {
    private int id;
    private String name;
    // default constructor, getters and setters omitted
}

7.2. Modify the Mapper

7.2.修改映射器

Here we need to add a method to convert the Division to DivisionDTO and vice versa; if MapStruct detects that the object type needs to be converted and the method to convert exists in the same class, it will use it automatically.

这里我们需要添加一个方法,将Division转换为DivisionDTO,反之亦然;如果MapStruct检测到需要转换的对象类型和转换的方法存在于同一个类中,它将自动使用它。

Let’s add this to the mapper:

让我们把这个添加到映射器中。

DivisionDTO divisionToDivisionDTO(Division entity);

Division divisionDTOtoDivision(DivisionDTO dto);

7.3. Modify the Test Case

7.3.修改测试用例

Let’s modify and add a few test cases to the existing one:

让我们在现有的基础上修改并添加一些测试案例。

@Test
public void givenEmpDTONestedMappingToEmp_whenMaps_thenCorrect() {
    EmployeeDTO dto = new EmployeeDTO();
    dto.setDivision(new DivisionDTO(1, "Division1"));
    Employee entity = mapper.employeeDTOtoEmployee(dto);
    assertEquals(dto.getDivision().getId(), 
      entity.getDivision().getId());
    assertEquals(dto.getDivision().getName(), 
      entity.getDivision().getName());
}

8. Mapping With Type Conversion

8.带类型转换的映射

MapStruct also offers a couple of ready-made implicit type conversions, and for our example, we will try to convert a String date to an actual Date object.

MapStruct还提供了一些现成的隐式类型转换,在我们的例子中,我们将尝试把一个String日期转换成一个实际的Date对象。

For more details on implicit type conversion, check out the MapStruct reference guide.

关于隐式类型转换的更多细节,请查看MapStruct参考指南

8.1. Modify the Beans

8.1.修改豆类

We add a start date for our employee:

我们为我们的雇员添加一个开始日期。

public class Employee {
    // other fields
    private Date startDt;
    // getters and setters
}
public class EmployeeDTO {
    // other fields
    private String employeeStartDt;
    // getters and setters
}

8.2. Modify the Mapper

8.2.修改映射器

We modify the mapper and provide the dateFormat for our start date:

我们修改映射器,为我们的开始日期提供dateFormat


  @Mapping(target="employeeId", source = "entity.id")
  @Mapping(target="employeeName", source = "entity.name")
  @Mapping(target="employeeStartDt", source = "entity.startDt",
           dateFormat = "dd-MM-yyyy HH:mm:ss")
EmployeeDTO employeeToEmployeeDTO(Employee entity);

  @Mapping(target="id", source="dto.employeeId")
  @Mapping(target="name", source="dto.employeeName")
  @Mapping(target="startDt", source="dto.employeeStartDt",
           dateFormat="dd-MM-yyyy HH:mm:ss")
Employee employeeDTOtoEmployee(EmployeeDTO dto);

8.3. Modify the Test Case

8.3.修改测试用例

Let’s add a few more test cases to verify the conversion is correct:

让我们再添加一些测试案例来验证转换是否正确。

private static final String DATE_FORMAT = "dd-MM-yyyy HH:mm:ss";
@Test
public void givenEmpStartDtMappingToEmpDTO_whenMaps_thenCorrect() throws ParseException {
    Employee entity = new Employee();
    entity.setStartDt(new Date());
    EmployeeDTO dto = mapper.employeeToEmployeeDTO(entity);
    SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
 
    assertEquals(format.parse(dto.getEmployeeStartDt()).toString(),
      entity.getStartDt().toString());
}
@Test
public void givenEmpDTOStartDtMappingToEmp_whenMaps_thenCorrect() throws ParseException {
    EmployeeDTO dto = new EmployeeDTO();
    dto.setEmployeeStartDt("01-04-2016 01:00:00");
    Employee entity = mapper.employeeDTOtoEmployee(dto);
    SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
 
    assertEquals(format.parse(dto.getEmployeeStartDt()).toString(),
      entity.getStartDt().toString());
}

9. Mapping With an Abstract Class

9.使用抽象类的映射

Sometimes, we may want to customize our mapper in a way that exceeds @Mapping capabilities.

有时,我们可能想以一种超出@Mapping能力的方式来定制我们的映射器。

For example, in addition to type conversion, we may want to transform the values in some way, as in our example below.

例如,除了类型转换之外,我们可能还想以某种方式转换数值,就像我们下面的例子一样。

In such cases, we can create an abstract class and implement methods we want to have customized, and leave abstract those that should be generated by MapStruct.

在这种情况下,我们可以创建一个抽象类,并实现我们想要定制的方法,而将那些应该由MapStruct生成的方法抽象出来。

9.1. Basic Model

9.1.基本模型

In this example, we’ll use the following class:

在这个例子中,我们将使用下面这个类。

public class Transaction {
    private Long id;
    private String uuid = UUID.randomUUID().toString();
    private BigDecimal total;

    //standard getters
}

and a matching DTO:

和一个匹配的DTO。

public class TransactionDTO {

    private String uuid;
    private Long totalInCents;

    // standard getters and setters
}

The tricky part here is converting the BigDecimal total amount of dollars into a Long totalInCents.

这里棘手的部分是将BigDecimal totalamount of dollars转换为Long totalInCents

9.2. Defining a Mapper

9.2.定义一个映射器

We can achieve this by creating our Mapper as an abstract class:

我们可以通过创建我们的Mapper作为一个抽象类来实现这个目标。

@Mapper
abstract class TransactionMapper {

    public TransactionDTO toTransactionDTO(Transaction transaction) {
        TransactionDTO transactionDTO = new TransactionDTO();
        transactionDTO.setUuid(transaction.getUuid());
        transactionDTO.setTotalInCents(transaction.getTotal()
          .multiply(new BigDecimal("100")).longValue());
        return transactionDTO;
    }

    public abstract List<TransactionDTO> toTransactionDTO(
      Collection<Transaction> transactions);
}

Here, we’ve implemented our fully customized mapping method for a single object conversion.

在这里,我们已经实现了我们完全定制的映射方法,用于单一对象的转换。

On the other hand, we left the method, which is meant to map Collection to a List abstract, so MapStruct will implement it for us.

另一方面,我们留下了这个方法,它的目的是将Collection 映射到List 抽象,所以MapStruct 将为我们实现它。

9.3. Generated Result

9.3.产生的结果

Since we have already implemented the method to map a single Transaction into a TransactionDTO, we expect MapStruct to use it in the second method.

由于我们已经实现了将单个Transaction映射到TransactionDTO的方法,我们希望MapStruct在第二个方法中使用它。

The following will be generated:

将会产生以下内容。

@Generated
class TransactionMapperImpl extends TransactionMapper {

    @Override
    public List<TransactionDTO> toTransactionDTO(Collection<Transaction> transactions) {
        if ( transactions == null ) {
            return null;
        }

        List<TransactionDTO> list = new ArrayList<>();
        for ( Transaction transaction : transactions ) {
            list.add( toTransactionDTO( transaction ) );
        }

        return list;
    }
}

As we can see in line 12, MapStruct uses our implementation in the method that is generated.

正如我们在第12行看到的,MapStruct在生成的方法中使用了我们的实现。

10. Before-Mapping and After-Mapping Annotations

10.贴图前和贴图后的注释

Here’s another way to customize @Mapping capabilities by using @BeforeMapping and @AfterMapping annotations. The annotations are used to mark methods that are invoked right before and after the mapping logic.

下面是通过使用@BeforeMapping@AfterMapping注解来定制@Mapping功能的另一种方法。这些注解用于标记在映射逻辑之前和之后被调用的方法。

They are quite useful in scenarios where we might want this behavior to be applied to all mapped super-types.

在我们可能希望这个行为适用于所有映射的超类型的情况下,它们相当有用。

Let’s take a look at an example that maps the sub-types of Car ElectricCar and BioDieselCar to CarDTO.

让我们看看一个例子,它将Car ElectricCarBioDieselCar的子类型映射到CarDTO

While mapping, we would like to map the notion of types to the FuelType enum field in the DTO. Then after the mapping is done, we’d like to change the name of the DTO to uppercase.

在映射的时候,我们想把类型的概念映射到DTO中的FuelTypeenum字段。然后在映射完成后,我们想把DTO的名字改为大写。

10.1. Basic Model

10.1.基本模型

We’ll use the following classes:

我们将使用以下类。

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

Sub-types of Car:

汽车的子类型。

public class BioDieselCar extends Car {
}
public class ElectricCar extends Car {
}

The CarDTO with an enum field type FuelType:

具有枚举字段类型FuelTypeCarDTO

public class CarDTO {
    private int id;
    private String name;
    private FuelType fuelType;
}
public enum FuelType {
    ELECTRIC, BIO_DIESEL
}

10.2. Defining the Mapper

10.2.定义映射器

Now let’s proceed and write our abstract mapper class that maps Car to CarDTO:

现在让我们继续写我们的抽象映射器类,将Car映射到CarDTO

@Mapper
public abstract class CarsMapper {
    @BeforeMapping
    protected void enrichDTOWithFuelType(Car car, @MappingTarget CarDTO carDto) {
        if (car instanceof ElectricCar) {
            carDto.setFuelType(FuelType.ELECTRIC);
        }
        if (car instanceof BioDieselCar) { 
            carDto.setFuelType(FuelType.BIO_DIESEL);
        }
    }

    @AfterMapping
    protected void convertNameToUpperCase(@MappingTarget CarDTO carDto) {
        carDto.setName(carDto.getName().toUpperCase());
    }

    public abstract CarDTO toCarDto(Car car);
}

@MappingTarget is a parameter annotation that populates the target mapping DTO right before the mapping logic is executed in case of @BeforeMapping and right after in case of @AfterMapping annotated method.

@MappingTarget 是一个参数注解,在执行映射逻辑之前填充目标映射DTO @BeforeMapping的情况下,@AfterMapping注解的方法之后。

10.3. Result

10.3.结果

The CarsMapper defined above generates the implementation:

上面定义的CarsMapper生成了实现

@Generated
public class CarsMapperImpl extends CarsMapper {

    @Override
    public CarDTO toCarDto(Car car) {
        if (car == null) {
            return null;
        }

        CarDTO carDTO = new CarDTO();

        enrichDTOWithFuelType(car, carDTO);

        carDTO.setId(car.getId());
        carDTO.setName(car.getName());

        convertNameToUpperCase(carDTO);

        return carDTO;
    }
}

Notice how the annotated methods invocations surround the mapping logic in the implementation.

请注意注释的方法调用是如何围绕着实现中的映射逻辑的

11. Support for Lombok

11.对龙目岛的支持

In the recent version of MapStruct, Lombok support was announced. So, we can easily map a source entity and a destination using Lombok.

在最近的MapStruct版本中,宣布支持Lombok。因此,我们可以使用Lombok轻松地映射一个源实体和一个目的地。

To enable Lombok support, we need to add the dependency in the annotation processor path. Since Lombok version 1.18.16, we also have to add the dependency on lombok-mapstruct-binding. Now we have the mapstruct-processor as well as Lombok in the Maven compiler plugin:

为了启用Lombok支持,我们需要在注释处理器路径中添加依赖性。从Lombok 1.18.16版本开始,我们还需要添加对lombok-mapstruct-binding的依赖。现在我们有了mapstruct-processor,以及Maven编译器插件中的Lombok。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.5.1</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.4.2.Final</version>
            </path>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
	        <version>1.18.4</version>
            </path>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok-mapstruct-binding</artifactId>
	        <version>0.2.0</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

Let’s define the source entity using Lombok annotations:

让我们使用Lombok注解来定义源实体。

@Getter
@Setter
public class Car {
    private int id;
    private String name;
}

And the destination data transfer object:

以及目的地数据传输对象。

@Getter
@Setter
public class CarDTO {
    private int id;
    private String name;
}

The mapper interface for this remains similar to our previous example:

这方面的映射器接口仍然与我们之前的例子类似。

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

12. Support for defaultExpression

12.支持defaultExpression

Starting with version 1.3.0, we can use the defaultExpression attribute of the @Mapping annotation to specify an expression that determines the value of the destination field if the source field is null. This is in addition to the existing defaultValue attribute functionality.

从1.3.0版本开始,我们可以使用@Mapping注解的defaultExpression属性来指定一个表达式,该表达式决定了在源字段为null时目标字段的值。这是对现有defaultValue属性功能的补充。

The source entity:

源实体。

public class Person {
    private int id;
    private String name;
}

The destination data transfer object:

目标数据传输对象。

public class PersonDTO {
    private int id;
    private String name;
}

If the id field of the source entity is null, we want to generate a random id and assign it to the destination keeping other property values as-is:

如果源实体的id字段是null,我们要生成一个随机的id并将其分配给目的地,保持其他属性值不变。

@Mapper
public interface PersonMapper {
    PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
    
    @Mapping(target = "id", source = "person.id", 
      defaultExpression = "java(java.util.UUID.randomUUID().toString())")
    PersonDTO personToPersonDTO(Person person);
}

Let’s add a test case to verify the expression execution:

让我们添加一个测试案例来验证表达式的执行。

@Test
public void givenPersonEntitytoPersonWithExpression_whenMaps_thenCorrect() 
    Person entity  = new Person();
    entity.setName("Micheal");
    PersonDTO personDto = PersonMapper.INSTANCE.personToPersonDTO(entity);
    assertNull(entity.getId());
    assertNotNull(personDto.getId());
    assertEquals(personDto.getName(), entity.getName());
}

13. Conclusion

13.结论

This article provided an introduction to MapStruct. We’ve introduced most of the basics of the Mapping library and how to use it in our applications.

本文对MapStruct进行了介绍。我们已经介绍了Mapping库的大部分基础知识以及如何在我们的应用程序中使用它。

The implementation of these examples and tests can be found in the GitHub project. This is a Maven project, so it should be easy to import and run as it is.

这些例子和测试的实现可以在GitHub项目中找到。这是一个Maven项目,所以应该很容易导入并按原样运行。