“This is the 17th day of my participation in the Gwen Challenge in November. See details of the event: The Last Gwen Challenge in 2021”.
1. Introduction
If you’re still using Beanutils.copyProperties (), please switch to a mapping framework
2. Introduction
Creating large Java applications with multiple layers requires the use of multiple models, such as persistence models, domain models, or so-called Dtos. Using multiple models for different application layers would require us to provide a way to map beans to beans.
Doing this manually can quickly create a lot of boilerplate code and consume a lot of time. Fortunately, Java has multiple object mapping frameworks.
In this article, we compare the performance of the five most popular Java mapping frameworks.
3. Mapping framework
3.1. Dozer
Dozer is a mapping framework that uses recursion to copy data from one object to another. The framework not only replicates properties between beans, but also converts them automatically between different types.
To use the Dozer framework, we need to add such dependencies to our project:
<dependency>
<groupId>com.github.dozermapper</groupId>
<artifactId>dozer-core</artifactId>
<version>6.5.0</version>
</dependency>
Copy the code
3.2. Orika
Orika is a bean-to-bean mapping framework that recursively copies data from one object to another.
The Orika generally works like a bulldozer. The main difference between the two is that Orika is generated using bytecode. This allows faster mapper generation with minimal overhead.
To use it, we need to add such dependencies to our project:
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.4</version>
</dependency>
Copy the code
3.3. MapStruct
MapStruct is a code generator that automatically generates bean mapper classes.
MapStruct also has the ability to convert between different data types.
To add MapStruct to our project, we need to include the following dependencies:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.1. The Final</version>
</dependency>
Copy the code
3.4. ModelMapper
ModelMapper is a framework designed to simplify object mapping by determining how objects map to each other by convention. It provides an API for type safety and refactoring safety.
To include ModelMapper in our project, we need to add the following dependencies:
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.8</version>
</dependency>
Copy the code
3.5. JMapper
JMapper is a mapping framework designed to provide an easy-to-use, high-performance mapping between Java beans.
The framework aims to apply the DRY principle using annotations and relational mapping.
The framework supports different configurations: annotation-based, XML-based, or API-based.
To include JMapper in our project, we need to add its dependencies:
<dependency>
<groupId>com.googlecode.jmapper-framework</groupId>
<artifactId>jmapper-core</artifactId>
<version>1.6.1. CR2</version>
</dependency>
Copy the code
4. Simple implementation
4.1. Orika Converter
Orika allows full API implementation, which greatly simplifies mapper creation:
public class OrikaConverter implements Converter{
private MapperFacade mapperFacade;
public OrikaConverter(a) {
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(Order.class, SourceOrder.class).field("orderStatus"."status").byDefault().register();
mapperFacade = mapperFactory.getMapperFacade();
}
@Override
public Order convert(SourceOrder sourceOrder) {
return mapperFacade.map(sourceOrder, Order.class);
}
@Override
public DestinationCode convert(SourceCode sourceCode) {
returnmapperFacade.map(sourceCode, DestinationCode.class); }}Copy the code
4.2. Dozer converter
Dozer requires an XML mapping file that contains the following sections:
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping https://dozermapper.github.io/schema/bean-mapping.xsd">
<mapping>
<class-a>com.baeldung.performancetests.model.source.SourceOrder</class-a>
<class-b>com.baeldung.performancetests.model.destination.Order</class-b>
<field>
<a>status</a>
<b>orderStatus</b>
</field>
</mapping>
<mapping>
<class-a>com.baeldung.performancetests.model.source.SourceCode</class-a>
<class-b>com.baeldung.performancetests.model.destination.DestinationCode</class-b>
</mapping>
</mappings>
Copy the code
After defining the XML map, we can use it from our code:
public class DozerConverter implements Converter {
private final Mapper mapper;
public DozerConverter(a) {
this.mapper = DozerBeanMapperBuilder.create().withMappingFiles("dozer-mapping.xml").build();
}
@Override public Order convert(SourceOrder sourceOrder) {
return mapper.map(sourceOrder,Order.class);
}
@Override public DestinationCode convert(SourceCode sourceCode) {
returnmapper.map(sourceCode, DestinationCode.class); }}Copy the code
4.3. MapStruct converter
MapStruct is very simple to define because it is based entirely on code generation:
@Mapper
public interface MapStructConverter extends Converter {
MapStructConverter MAPPER = Mappers.getMapper(MapStructConverter.class);
@Mapping(source = "status", target = "orderStatus")
@Override
Order convert(SourceOrder sourceOrder);
@Override
DestinationCode convert(SourceCode sourceCode);
}
Copy the code
More use cases can be found in an article I wrote earlier: elegant Object Transformations -MapStruct
4.4. JMapperConverter
JMapperConverter needs more work. After implementing the interface:
public class JMapperConverter implements Converter {
JMapper realLifeMapper;
JMapper simpleMapper;
public JMapperConverter(a) {
JMapperAPI api = new JMapperAPI().add(JMapperAPI.mappedClass(Order.class));
realLifeMapper = new JMapper(Order.class, SourceOrder.class, api);
JMapperAPI simpleApi = new JMapperAPI().add(JMapperAPI.mappedClass(DestinationCode.class));
simpleMapper = new JMapper( DestinationCode.class, SourceCode.class, simpleApi);
}
@Override
public Order convert(SourceOrder sourceOrder) {
return (Order) realLifeMapper.getDestination(sourceOrder);
}
@Override
public DestinationCode convert(SourceCode sourceCode) {
return(DestinationCode) simpleMapper.getDestination(sourceCode); }}Copy the code
We also need to add @jmap annotations for each field of the target class. In addition, JMapper cannot convert between enumerated types by itself, requiring us to create custom mapping functions:
@JMapConversion(from = "paymentType", to = "paymentType")
public PaymentType conversion(com.baeldung.performancetests.model.source.PaymentType type) {
PaymentType paymentType = null;
switch(type) {
case CARD:
paymentType = PaymentType.CARD;
break;
case CASH:
paymentType = PaymentType.CASH;
break;
case TRANSFER:
paymentType = PaymentType.TRANSFER;
break;
}
return paymentType;
}
Copy the code
4.5. ModelMapper converter
ModelMapperConverter asks us to provide only the classes we want to map:
public class ModelMapperConverter implements Converter {
private ModelMapper modelMapper;
public ModelMapperConverter(a) {
modelMapper = new ModelMapper();
}
@Override
public Order convert(SourceOrder sourceOrder) {
return modelMapper.map(sourceOrder, Order.class);
}
@Override
public DestinationCode convert(SourceCode sourceCode) {
returnmodelMapper.map(sourceCode, DestinationCode.class); }}Copy the code
5. The last
You can see that no matter which mapping framework you use, you have more code than beanutils.copyProperties (), but much better performance than beanutils.copyProperties (), which uses reflection, Poor performance.
The author personally recommends using mapStruct, which generates enhanced code during compilation and has higher performance.