Site icon Vinsguru

MicroServices – DTO to Entity & Entity to DTO Mapping – Libraries Comparison

Overview:

In this article, I would like to show you the performance of few libraries when we do the Entity to DTO conversion or vice versa.

Goal:

I have an Entity class as shown here.

public class Car {

    private long id;
    private String make;
    private int numOfSeats;
    private Date releaseDate;
    private Engine engine;
    
    //getters & setters

}
public class Engine {
    private String type;
    
    //getters & setters
}

I have below corresponding DTO classes.

public class CarDto {

    private long id;
    private String make;
    private int numOfSeats;
    private Date releaseDate;
    private EngineDto engineDto;
    
    //getters & setters
}
public class EngineDto {
    private String type;

    //getters & setters
}

Our goal is to convert that Entity to DTO 10 million times to see how long it takes to do the conversion using some common libraries. Why 10 million? We can not do just 1 conversion and measure the time. So, I simply chose 10 million conversions.

Entity Object Creation:

Our aim is to measure only the entity to dto conversion. So lets create a simple car entity instance as shown here upfront. We would be using the same Car entity and convert that into CarDto 10 million times.

//create Car entity
Car car = new Car();
car.setId(1);
car.setMake("Honda");
car.setNumOfSeats(4);
Date date = Date.from(LocalDate.of(2010, 10, 10).atStartOfDay(ZoneId.systemDefault()).toInstant());
car.setReleaseDate(date);
//create Engine enity
Engine engine = new Engine();
engine.setType("V6");
car.setEngine(engine);

// we use mapping libraries here

Model Mapper:

Lets first play with model mapper.

<dependency>
  <groupId>org.modelmapper</groupId>
  <artifactId>modelmapper</artifactId>
  <version>2.3.0</version>
</dependency>
ModelMapper modelMapper = new ModelMapper();
TypeMap<Car, CarDto> typeMap = modelMapper.createTypeMap(Car.class, CarDto.class);
typeMap.addMappings(mapper -> {
  mapper.map(Car::getEngine, CarDto::setEngineDto);
});
long time1 = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; i++) {
  CarDto carDto = modelMapper.map(car, CarDto.class);
}
long time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
Run 1: 60285 ms
Run 2: 58734 ms

Spring BeanUtils:

Lets do the same type conversion using Spring bean utils.

long time1 = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; i++) {
  CarDto carDto = new CarDto();
  EngineDto engineDto = new EngineDto();
  BeanUtils.copyProperties(car.getEngine(), engineDto);
  BeanUtils.copyProperties(car, carDto);
  carDto.setEngineDto(engineDto);
}
long time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
Run 1: 6045 ms
Run 2: 6372 ms

MapStruct:

Lets do the conversion using MapStruct library. MapStruct is simple to use. but not so easy as the other above libs.

<dependency>
  <groupId>org.mapstruct</groupId>
  <artifactId>mapstruct</artifactId>
  <version>1.3.0.Final</version>
</dependency>
<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.3.0.Final</version>
      </path>
    </annotationProcessorPaths>
  </configuration>
</plugin>
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface CarMapper {

    CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );

    @Mapping(source = "engine", target = "engineDto")
    CarDto toDto(Car car);

    EngineDto toDto(Engine engine);

}
long time1 = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; i++) {
  CarDto carDto = CarMapper.INSTANCE.toDto(car);
}
long time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
Run 1: 174 ms
Run 2: 180 ms

The result is very impressive and it is in milliseconds.  MapStruct is able to convert Entity object to Dto 10 million times within 200 milliseconds!!

MapStruct creates this class at compile time using the Mapper interface we have created.

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2019-09-01T02:17:45+0000",
    comments = "version: 1.3.0.Final, compiler: javac, environment: Java 12.0.2 (Oracle Corporation)"
)
public class CarMapperImpl implements CarMapper {

    @Override
    public CarDto toDto(Car car) {
        if ( car == null ) {
            return null;
        }
        CarDto carDto = new CarDto();
        carDto.setEngineDto( toDto( car.getEngine() ) );
        carDto.setId( car.getId() );
        carDto.setMake( car.getMake() );
        carDto.setNumOfSeats( car.getNumOfSeats() );
        carDto.setReleaseDate( car.getReleaseDate() );
        return carDto;
    }

    @Override
    public EngineDto toDto(Engine engine) {
        if ( engine == null ) {
            return null;
        }
        EngineDto engineDto = new EngineDto();
        engineDto.setType( engine.getType() );
        return engineDto;
    }
}

Manual Conversion:

Lets ignore all the libraries and do the Entity to DTO conversion manually ourselves. Lets see how long it takes for the same test we have been doing.

long time1 = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; i++) {
  EngineDto engineDto = new EngineDto();
  engineDto.setType(car.getEngine().getType());
  CarDto carDto = new CarDto();
  carDto.setId(car.getId());
  carDto.setMake(car.getMake());
  carDto.setNumOfSeats(car.getNumOfSeats());
  carDto.setReleaseDate(car.getReleaseDate());
  carDto.setEngineDto(engineDto);
}
long time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
Run 1: 172 ms
Run 2: 188 ms

Code Samples:

All these test are available in GitHub.

Summary:

There are many libraries out there to do this conversion. I picked just few for comparison. Among the 3 I had chosen, MapStruct seems to do the job exceptionally well. It is because it does not use run time java reflection APIs. Instead the code for doing the above mapping is getting generated at compile time. In fact the code it creates it is very close to what we have had for the manual conversion. So the performance of MapStruct is very close to manual conversion.

Share This:

Exit mobile version