MapStruct Mapper
Introduction
MapStruct is used For converting the Entity to DTO.
DB output -> mapper/transformer/convertor -> VIEW
Common use case is when the data is retrieved from DB and the response from the microservice is expected in a different format or with less or more number of fields, then map struct can be utilized for conversion.
If DB Output is
{
"first-name" : "Lorem",
"middle-name" : "k",
"last-name" : "ipsum"
}
and VIEW (dito/angular UI) needs the following o/p
{
"fName" : "Lorem k"
"lName" : "ipsum"
}
Call a custom method defined in the @Mapping
annotation with qualifiedByName
attribute
OR
Create an Annotation for it –> Ref: Custom Mapper Annotation
- target → Target DTO Class variable name
- source → Source DB Entity class variable name
- Mapping from Source DB Entity to Target DTO entity
Implement the methods from expression
(processName method here in the example)
or qualifiedByName
properties (processNoData method)
Retrieving a mapper
Mapper Factory
Mappers.getMapper(TestMapper.class)
@Mapper(componentModel = "default")//componentModel is optional for default
public interface TestMapper {
...
}
Calling Class can use the Mappers factory to retrieve the relevant object
import org.mapstruct.factory.Mappers;
final TestMapper mapper = Mappers.getMapper(TestMapper.class);
Dependency Injection
@Mapper(componentModel = “spring”)
- Using Dependency Injection - Spring framework
Specify the component model to which the generated mapper should adhere.
Supported values are
- default: the mapper uses no component model, instances are typically retrieved via Factory Mappers.getMapper(Class)
- spring: the generated mapper is a Spring bean and can be retrieved via
@Autowired
Field Mapping
Mapping Fields With Different Field Names between Source and Target Objects
@Mapper
public interface EmployeeMapper {
@Mapping(target = "employeeName", source = "entity.name")
@Mapping(target = "dateOfBirth", source = "entity.dob")
@Mapping(target = "phones", source = "entity.phones")
@Mapping(target = "addresses", source = "entity.addresses")
EmployeeDto employeeToEmployeeDto(Employee entity);//This will be implemented by MapStruct
}
// Target = EmployeeDto , Source = Employee
Employee | EmployeeDto |
---|---|
The reverse is also possible. The data from UI form or from some client, can be prepared to be inserted into DB from DTO to DB Entity.
Multiple source parameters
When the Target Object class comprises fields which is a combination of multiple objects
Data Type conversion
Applying formnatting
dateFormat
Applying the dateFormat
attribute
@Mapping(target = "birthDate", source = "employee.dob", dateFormat = "dd-MM-yyyy HH:mm:ss")
Input | Output |
---|---|
“dateOfBirth”: 1676878272857 | “birthDate” : “20-02-2023 01:31:12” |
numberFormat
Similarly Applying the numberFormat
attribute
@Mapping(target="test", source="tester.test", numberFormat = "₹#.00")
Input | Output |
---|---|
“tester” : 52.32 | “test” : “₹52.32” |
Explicit conversion with Expressions
Converting a piece of data from one type to another
defaultExpression
@Mapping annotation has a defaultExpression attribute. It determines the value of the destination field if the source field is null
@Mapping(target = "extraField", source = "employee.nullTester", defaultExpression = "java(com.github.javafaker.Faker.instance().chuckNorris().fact())")
Input is a null string | Output is a string based on Faker.instance().chuckNorris().fact() |
---|---|
qualifiedByName
Use the qualifiedByName attribute of @Mapping Annotation to invoke the java method to take more control
@Mapping(target = "phones", source = "employee.phones", qualifiedByName = "processPhoneMap")//Map to List
//Implement the method qualified with processPhoneMap
@Named("processPhoneMap")
List<String> processPhoneMap(Map<String, String> phoneMap) {
...
//Change the Map to a List
}
Input is a map of phone numbers | Output is a List of Strings |
---|---|
Mapping a Map into a Bean(Pojo)
In Case the data from JSON is not read into a JavaObject but instead into a Map using TypeReference
Map<String, String> jsonAsMap = objectMapper.readValue(url, new TypeReference<Map<String, String>>() {});
TesterDto testerDto = mapper.testMapperFromMap(jsonAsMap);
The mapper can be written in such a way, that the value of the Map is mapped with the POJO it is being mapped