Last updated: 2023-03-07
Using MapStruct with Maven and Lombok
MapStruct is a Java library to simplify data transfer between classes and avoid writing boilerplate code. The following article will show the steps to automate the mapping between a JPA entity and a DTO in a Spring Boot application.
Backgrounds
For our example, we use the following technologies:
- Spring Boot 3.4.1
- Maven
- Lombok
- MapStruct
As developers, all we need to do is provide an interface that defines the desired mapping methods. MapStruct then generates the implementing class at build time - so our initial setup is a bit more extensive.
Maven setup
In our pom.xml, we first need to add the dependency that will later allow us to define our mapper.
Furthermore, we need to extend the maven-compiler-plugin
to activate the code generation of MapStruct.
Lombok is our first annotation processor, followed directly by MapStruct. Another reference to lombok-mapstruct-binding
is necessary for these two libraries to work together. Without Lombok, only the mapstruct-processor
would be needed at this point.
Mapper interface
Now let's get to the core of our example - the mapper. In our example, there is a "CarPart" entity that has a primary key, two data fields as well as a reference to a "Supplier".
Our JPA Entity example
Our DTO contains all fields of the entity with further annotations for validation when used in a RestController
. The reference to the Supplier is specified in form of the foreign key id.
Our example DTO
With these classes in place, we can now define the first version of our mapper. The "CarPartMapper" contains two methods for mapping in both directions. By annotating the interface with @Mapper
, MapStruct will parse it and provide an implementing class. With componentModel = MappingConstants.ComponentModel.SPRING
the class is added to Spring's application context and can be referenced later on in our service using @Autowired
.
First version of our Mapper
The provided methods are automatically parsed by MapStruct. One parameter is the source object and the parameter with @MappingTarget
defines the target object. Without a @MappingTarget
the target object would be newly initialized; this however isn' t desired in our case. The class generated by MapStruct will automatically map all fields with the same name - in our case id, typeCode and releaseDate.
With the @Mapping
annotation we can add special handlings for single fields. Since the id field is automatically generated by our persistence layer (GenerationType.IDENTITY
), it should not be taken from the DTO to the entity. For the reference to the Supplier we want to add a custom handling, so this field should be ignored as well.
Mapping the foreign key
Lastly, we want to handle the reference to the Supplier by adding the following two default methods to our interface:
Adding custom behaviour with @AfterMapping
By annotating them with @AfterMapping
we tell MapStruct to call the method after the initial mapping. With @Context
we can add additional parameters to the method - we use this ability here to provide the SupplierRepository. If the specified Supplier does not exist, a 404
error should be returned.
With this, our mapper is ready and can be used in our service.
Using our new Mapper for creating a CarPart
Bootify provides a free tool to create a Spring Boot application with a custom database schema and REST API. The Professional plan also provides an option for MapStruct, so the required mappers are automatically generated and added to your code base.