Simplify Model Mapping in Spring Boot with MapStruct and Lombok

Mayank Yaduvanshi
3 min readJun 29, 2023

Introduction:

In Spring Boot applications, one common task is mapping data between different layers of an application, such as DTOs (Data Transfer Objects) and entities. This mapping process can be repetitive and time-consuming, but luckily, there are powerful libraries available to simplify the task. In this blog, we’ll explore how to leverage MapStruct and Lombok together to streamline mapping operations in a Spring Boot project.

What is MapStruct?

MapStruct — Java bean mappings, the easy way!

MapStruct is a Java-based code generation library that automates the process of mapping between Java beans. It generates type-safe mapping code at compile time, eliminating the need for manual mapping code. MapStruct supports various mapping strategies, including property-based mapping, mapping with expressions, and custom converters.

Why use Lombok with MapStruct?

Project Lombok

Lombok is another popular Java library that helps reduce boilerplate code by automatically generating getter, setter, equals, hashCode, and other methods. When used in conjunction with MapStruct, Lombok can further simplify mapping code by eliminating the need to write constructor code, getters, and setters for the mapped classes.

Setting Up the Project:

To get started, create a new Spring Boot project or use an existing one. Make sure you have the necessary dependencies in your pom.xml or build.gradle file:

<!-- Maven -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
// Gradle
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
compileOnly 'org.projectlombok:lombok:1.18.20'

Configure mapstruct to work with lombok:

To generate mapping correctly using mapstruct while also using lombok requires an extra step of configuration, else it will not generate the mappings at all.

Add plugin maven-compiler-plugin and configure it to use lombok-mapstruct-bindings:

// pom.xml

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>
</plugin>

Defining Mappings:

Let’s assume we have two classes: Product and ProductDto. The Product class represents the entity in our application, while the ProductDto class is used for data transfer purposes.

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private Long id;
private String name;
private String description;
private BigDecimal price;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductDto {
private Long id;
private String name;
private String description;
private String formattedPrice;
}

Using MapStruct and Lombok:

To generate the mapping code between Product and ProductDto, we need to define a mapper interface and annotate it with @Mapper from MapStruct. Additionally, annotate the mapper interface with @Mapper(componentModel = “spring”) to make it a Spring component.

@Mapper(componentModel = "spring")
public interface ProductMapper {
ProductMapper INSTANCE = Mappers.getMapper(ProductMapper.class);

@Mapping(source = "price", target = "formattedPrice")
ProductDto toDto(Product product);
}

With Lombok, we can simplify the ProductDto class by removing the explicit constructor and getters/setters:

@Data
public class ProductDto {
private Long id;
private String name;
private String description;
private String formattedPrice;
}

Using the Mapper:

Now that we have defined the mapper interface, we can use it in our Spring Boot application. Inject the ProductMapper into the desired component or service and use it to perform the mapping.


@Service
public class ProductService {
private final ProductMapper productMapper;

public ProductService(ProductMapper productMapper) {
this.productMapper = productMapper;
}

public ProductDto getProductDto(Product product) {
return productMapper.toDto(product);
}
}

In the above example, the getProductDto() method takes a Product instance and maps it to a ProductDto using the ProductMapper interface. The mapped ProductDto object is then returned.

Conclusion:

By combining the power of MapStruct and Lombok in a Spring Boot project, we can significantly simplify the mapping process. MapStruct generates the mapping code at compile time, reducing manual effort and ensuring type safety. Lombok eliminates boilerplate code, further enhancing the developer experience. Together, these libraries provide a clean and efficient way to handle complex mapping scenarios in Spring Boot applications.

Remember to configure your IDE to enable annotation processing for Lombok and rebuild your project to ensure that the mapping code is generated correctly.

With MapStruct and Lombok, you can focus on the business logic of your Spring Boot application, leaving the mapping code to be generated automatically. This approach improves code readability, maintainability, and developer productivity, making your Spring Boot development experience even more enjoyable.

--

--

Mayank Yaduvanshi

Full Stack Software Developer, Experienced in Angular, Flutter, Spring boot, NodeJs, MySQL, MongoDB, AWS, Google Cloud, Docker, Git, JIRA.