Normal mapping
public class Mapper {
public Employee toEmployee(EmployeeDTO dto) {
Employee employee=new Employee();
employee.setName(dto.getName());
employee.setJobTitle(dto.getJobTitle());
employee.setJoinedDate(dto.getJoinedDate());
employee.setMobileNo(dto.getMobileNo());
employee.setDepartment(dto.getDepartment());
employee.setSalary(dto.getSalary());
return employee;
}
}
what is mapstruck?
MapStruct is a code generator that greatly simplifies the implementation of mappings between Java models and DTO. Using Mapstruck we don’t have to long mapper code instead simply we need to say source and destination class.
MapStruck automatically generates mapper code which maps fields based on name from source to destination class also we can pass custom mapping for field.
mapstruck maven dependency
<org.mapstruct.version>1.5.2.Final</org.mapstruct.version>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
if you are using Lombok to reduce boilerplate cod in entity or DTO class then add below in pom.xml
How to create a Mapper in mapstruck?
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</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>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Create an interface and annotate with @Mapper. Now let's define a method. The method parameter acts as source and method return type acts as Target. MapStruck will map source fields to target fields if they have the same field name.
Mapping fields with same name
By default, MapStruck maps fields by name if source and target have fields with same name then mapper maps the field. The EmployeeDTO and Employee both classes have the same property names.
@Mapper(componentModel = "spring")
public interface EmployeeMapper {
Employee toEmployee(EmployeeDTO employeeDTO);
}
The componentModel = “spring” define this mapper as spring bean which we can autowire in our application.
The generated class code will look like below the source and target classes both having the same property names.
@Component
public class EmployeeMapperImpl implements EmployeeMapper {
@Override
public Employee toEmployee(EmployeeDTO employeeDTO) {
if ( employeeDTO == null ) {
return null;
}
Employee employee = new Employee();
employee.setDepartment( employeeDTO.getDepartment() );
employee.setGender( employeeDTO.getGender() );
employee.setId( employeeDTO.getId() );
employee.setJobTitle( employeeDTO.getJobTitle() );
employee.setJoinedDate( employeeDTO.getJoinedDate() );
employee.setMobileNo( employeeDTO.getMobileNo() );
employee.setName( employeeDTO.getName() );
employee.setSalary( employeeDTO.getSalary() );
return employee;
}
}
Mapping fields with different name
In most of the cases, the source and target classes will not have same field names so now how to map these fields for this we have @Mapping annotation which take the target field name and soruce field name map these field. We can give custom mapping logic for fields in @Mapping annotation.
@Mappings takes group of @Mapping annotation if there is more mapping logic than its better give them inside @Mappings.
public interface EmployeeMapper {
@Mappings({
@Mapping(target = "jobTitle", source = "title"),
@Mapping(target = "joinedDate", source = "startingdate")
})
Employee toEmployee(EmployeeDTO employeeDTO);
}
Mapping nested beans fields
Here we will see how we can map nested object fields if you see example below the Employee class has Personal object which has name, gender, age and Address object.
We have to give full path to the field example personal.address.streetaddress in target, so mapper will search for streetaddress inside address in personal object.
@Mappings({
@Mapping(target = "personal.name", source = "firstName"),
@Mapping(target = "personal.gender", source = "gender"),
@Mapping(target = "personal.age", source = "age"),
@Mapping(target = "personal.address.streetaddress", source = "dto.primaryAddr.line1"),
@Mapping(target = "personal.address.city", source = "dto.primaryAddr.city"),
@Mapping(target = "personal.address.postalcode", source = "dto.primaryAddr.postalCode")
})
Employee toEmployee(EmployeeDTO dto);
Mapping custom method
In some cases it can be required to manually implement a specific mapping from one type to another which can’t be generated by MapStruct. One way to handle this is to implement the custom method on another class which then is used by mappers generated by MapStruct
Define java 8 default method in the interface and provide the implementation. We use @Named to name the method.
In the @Mapping define the source and target, give the @Named method name in the qualifiedByName attributes so the mapstruck will generate the code and call the default method
package com.example.graphql.mapstruck;
import java.util.ArrayList;
import java.util.List;
@Mapper(componentModel = "spring")
public interface EmployeeMapper {
@Mapping(target="primaryAddr",source ="personal.address",qualifiedByName = "addressdto" )
EmployeeDTO toEmployeeDto(Employee employee);
@Named("addressdto")
default PrimaryAddr composeAddress(Address address) {
PrimaryAddr primaryAddr=new PrimaryAddr();
primaryAddr.setLine1(address.getStreetaddress());
primaryAddr.setCity(address.getCity());
primaryAddr.setPostalCode(address.getPostalcode());
return primaryAddr;
}
}
Let see how mapstruck have generated the mapper code for the above custom method.The generated mapper code internally call our custom method for mapping address.
public class EmployeeMapperImpl implements EmployeeMapper {
@Override
public EmployeeDTO toEmployeeDto(Employee employee) {
if ( employee == null ) {
return null;
}
EmployeeDTO employeeDTO = new EmployeeDTO();
employeeDTO.setPrimaryAddr( composeAddress( employeePersonalAddress( employee ) ) );
return employeeDTO;
}
private Address employeePersonalAddress(Employee employee) {
if ( employee == null ) {
return null;
}
Personal personal = employee.getPersonal();
if ( personal == null ) {
return null;
}
Address address = personal.getAddress();
if ( address == null ) {
return null;
}
return address;
}
}
Top comments (0)