MapStruct — An Entity Mapping Solution

Image for post
Image for post

Tired of writing the code for mapping beans to entities and vice versa over and over again? Here comes MapStruct to the rescue!

MapStruct in a line is a code generator that simplifies bean mappings.

Let’s get straight down to the nitty-gritty details! The example assumes we are using Spring Boot with maven.

First, we begin with adding the dependencies to the pom.xml file

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.1.0.Final</version>
</dependency>

MapStruct also requires a processor plugin to be added to pom.xml. The mapstruct-processor is used to generate the mapper implementation during the build phase.

<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.1.0.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

Now we move on the process of creating a mapper interface for our bean and entity.

We create a simple entity:

@Entity
public class Employee{
private Long empId;
private String name;
//Getter Setter omitted for brevity
}

Then we create a simple Data Transfer Object(DTO):

public class EmployeeDTO {
private Long empId;
private String name;
//Getter Setter omitted for brevity
}

We then create a mapper interface for the mappings:

@Mapper(componentModel="spring")
public interface EmployeeMapper {
EmployeeMapper INSTANCE = Mappers.getMapper(EmployeeMapper.class);
EmployeeDTO employeeToEmployeeDTO(Employee entity);
Employee employeeDTOToEmployee(EmployeeDTO dto);
}

The componentModel is utilized for Spring’s IoC, for the Mapper to be found and instantiated as a bean.The INSTANCE is created to use the mapper methods.

We can generate the mapper by running the mvn:clean install command

The generated mapper can be found under target > generated sources>package hierarchy

We can use the mapper as follows :

EmployeeDTO temp =  
EmployeeMapper.INSTANCE.employeeToEmployeeDTO(entity);

Now that the basic mapper usage is out of the way, let’s check out some specific use cases:

  1. The source and target member names are different:
@Mapper(componentModel="spring")
public interface EmployeeMapper {
EmployeeMapper INSTANCE = Mappers.getMapper(EmployeeMapper.class);
@Mappings({
@Mapping(target="employeeName", source="entity.name")
})
EmployeeDTO employeeToEmployeeDTO(Employee entity);
@Mappings({
@Mapping(target="name", source="dto.employeeName")
})
Employee employeeDTOToEmployee(EmployeeDTO dto);
}

The above code assumes the DTO has the field employeeName and the entity has the field name.
The @Mappings & @Mapping annotation is used to specify target and source for mapping if their names are different.

2. Entity is composite and has another object inside it which needs a mapper of itself

@Entity
public class Employee{
private Long empId;
private String name;
private Department dept; // another entity
//Getter Setter omitted for brevity
}
@Entity
public class Department{
private Long deptId;
private String name;
//Getter Setter omitted for brevity
}
------------------------------------------------------------------
public class EmployeeDTO {
private Long empId;
private String name;
private DepartmentDTO dept;
//Getter Setter omitted for brevity
}
public class DepartmentDTO{
private Long deptId;
private String name;
//Getter Setter omitted for brevity
}

The mapper will be defined as:

@Mapper(componentModel="spring")
public interface OrgMapper {
OrgMapper INSTANCE = Mappers.getMapper(OrgMapper.class);
@Mappings({
@Mapping(target="employeeName", source="entity.name")
})
EmployeeDTO employeeToEmployeeDTO(Employee entity);
@Mappings({
@Mapping(target="name", source="dto.employeeName")
})
Employee employeeDTOToEmployee(EmployeeDTO dto);
}
//Department mapper
DepartmentDTO departmentToDepartmentDTO(department entity);
Department departmentDTOToDepartment(DepartmentDTO assetsDTO);
}

By defining the mappers of the contained class here, MapStruct automatically includes it in while creating the mapper code. A similar structure can be used in case of multiple classes within a class.
Another way of doing this if you want to segregate your mappers for different entities is :

@Mapper(componentModel="spring",uses=DepartmentMapper.class)
public interface EmployeeMapper {
EmployeeMapper INSTANCE = Mappers.getMapper(EmployeeMapper.class);
@Mappings({
@Mapping(target="employeeName", source="entity.name")
})
EmployeeDTO employeeToEmployeeDTO(Employee entity);
@Mappings({
@Mapping(target="name", source="dto.employeeName")
})
Employee employeeDTOToEmployee(EmployeeDTO dto);
}
@Mapper(componentModel="spring")
public interface DepartmentMapper{
DepartmentMapper INSTANCE =
Mappers.getMapper(DepartmentMapper.class);
DepartmentDTO departmentToDepartmentDTO(department entity);
Department departmentDTOToDepartment(DepartmentDTO assetsDTO);
}

The uses attribute is used to specify other mappers the given mapper needs, it can be used to specify multiple mappers too, within curly braces {}.

Hope this helps someone beginning out their journey with MapStruct. That’s all folks! These were the use cases i encountered in my ongoing skirmish with MapStruct! Stay tuned for more!

Written by

An avid reader and voracious consumer of knowledge. Striving to be better every day. Knows some tech stuff too you know!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store