MapStruct Mapper

3 minute read

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"
}

Pattern used in the example

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

Refer article

Refer Another Article