[Awesome-Java MapStruct] Using MapStruct to generate mapping code between Java bean types on Spring Boot

MapStruct

MapStruct is a code generator that greatly simplifies the implementation of mappings between Java bean types based on a convention over configuration approach.

The generated mapping code uses plain method invocations and thus is fast, type-safe and easy to understand.

MapStruct is an annotation processor which is plugged into the Java compiler and can be used in command-line builds (Maven, Gradle etc.) as well as from within your preferred IDE.

MapStruct uses sensible defaults but steps out of your way when it comes to configuring or implementing special behavior.

Installation

Gradle

When using a modern version of Gradle (>= 4.6), you add something along the following lines to your build.gradle:

1
2
3
4
5
6
7
dependencies {
// Installation – MapStruct
// https://mapstruct.org/documentation/installation/
implementation 'org.mapstruct:mapstruct:1.4.2.Final'

annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}

Apache Maven

If you’re using Maven to build your project add the following to your pom.xml to use MapStruct:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<properties>
<org.mapstruct.version>1.4.2.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> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

Usages

PO and DTO

Let’s assume we have a class representing cars (e.g. a JPA entity) and an accompanying data transfer object (DTO).

1
2
3
4
5
6
7
8
public class Car {

private String make;
private int numberOfSeats;
private CarType type;

//constructor, getters, setters etc.
}
1
2
3
4
5
6
7
8
public class CarDto {

private String make;
private int seatCount;
private String type;

//constructor, getters, setters etc.
}

The mapper interface

To generate a mapper for creating a CarDto object out of a Car object, a mapper interface needs to be defined:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

// The @Mapper annotation marks the interface as mapping interface and lets the MapStruct processor kick in during compilation.
// Here the componentModel attribute will help to inject the object of this interface as an Autowired dependency in other classes.
@Mapper(componentModel = "spring")
public interface CarMapper {

// The actual mapping method expects the source object as parameter and returns the target object. Its name can be freely chosen.
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

// An instance of the interface implementation can be retrieved from the Mappers class. By convention, the interface declares a member INSTANCE, providing clients access to the mapper implementation.
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}

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

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

Using the mapper

Based on the mapper interface, clients can perform object mappings in a very easy and type-safe manner:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void shouldMapCarToDto() {
//given
Car car = new Car( "Morris", 5, CarType.SEDAN );

//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );

//then
assertThat( carDto ).isNotNull();
assertThat( carDto.getMake() ).isEqualTo( "Morris" );
assertThat( carDto.getSeatCount() ).isEqualTo( 5 );
assertThat( carDto.getType() ).isEqualTo( "SEDAN" );
}

IDE Support

IntelliJ IDEA

Depending on how you configured the annotation processor in your Maven or Gradle project, IntelliJ may or may not pick it up automatically. You might need to make sure of it yourself in the project configuration.

There is an IntelliJ plugin for MapStruct support, that you can find in the Jetbrains plugins repository here MapStruct Support - IntelliJ IDEA & Android Studio Plugin | Marketplace - https://plugins.jetbrains.com/plugin/10036-mapstruct-support. The plugin is open source and you can report bugs and feature requests here on GitHub.

References

[1] MapStruct – Java bean mappings, the easy way! - https://mapstruct.org/

[2] Installation – MapStruct - https://mapstruct.org/documentation/installation/

[3] Reference Guide – MapStruct - https://mapstruct.org/documentation/reference-guide/

[3] MapStruct Support - IntelliJ IDEA & Android Studio Plugin | Marketplace - https://plugins.jetbrains.com/plugin/10036-mapstruct-support

[5] Quick Guide to MapStruct | Baeldung - https://www.baeldung.com/mapstruct