Orika
preface
There are many class replication tools, such as mapstruct, spring bean utils, Apache bean utils, dozer, etc. at present, mapstruct is used in my project team. In terms of performance, mapstruct is undoubtedly the best, because mapstruct copies attribute values through getter and setter methods, while other frameworks use reflection to copy more or less, which will not be repeated here. However, mapstruct also has its shortcomings. Please see the following:
I wonder if you have ever written a java expression similar to the following when using mapstruct:
@Mapper public interface SmsTemplateConverter { SmsTemplateConverter SMS_TEMPLATE_CONVERTER = Mappers.getMapper(SmsTemplateConverter.class); @Mappings({ // Here, static methods can only be called through the full class name, otherwise the class cannot be injected into the compiled file @Mapping(target = "templateType", expression = "java(org.example.enums.SmsEnum.getTypeByCode(platformTemp.getTemplateType()))") }) SmsCompanyTemplateVO toSmsCompanyTemplateVO(SmsCompanyTemplate companyTemp, SmsPlatformTemplate platformTemp); }
It is not difficult to find that once the full class name directory of org.example.enums.SmsEnum is changed, the code here will report an error, because the expression here is a string. When the directory is changed, the full class name path cannot be automatically changed (because it is a string, not a real java code, and the Java expression of mapstruct is generated by the code generator and can be seen in the compiled target directory), it is written dead. It will be difficult to maintain and expand later. Therefore, we decided to give up mapstruct in our project.
After investigating many kinds of replication tools, I chose Orika and verified that it is feasible through the demo. Before learning about Orika, I might as well learn about the comparison of various types of replication tools, as shown in the following figure: (the picture originates from the network. If there is infringement, please contact to delete it)
Use example
Below, I will demonstrate the use of Orika with a basic example and an example of converting Date attribute to String attribute.
Import dependency
<!-- Class replication tool: orika --> <dependency> <groupId>ma.glasnost.orika</groupId> <artifactId>orika-core</artifactId> <version>1.5.2</version><!-- or latest version --> </dependency> <!-- hu-tool tool kit --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.16</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency>
Define the entity Person and Person dto
Person entity:
import lombok.Data; import java.util.Date; @Data public class Person { private String name; // Note that the field name is the same as person to private String age; private Date birth; }
PersonDTO entity:
import lombok.Data; @Data public class PersonDTO { private String name; private Integer dtoAge; private String dtoBirth; }
Basic example
Simple usage 1:
import ma.glasnost.orika.MapperFacade; import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.impl.DefaultMapperFactory; import org.example.entity.Person; import org.example.entity.PersonDTO; // MapperFacade public class MapperFacadeMain { public static void main(String[] args) { MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build(); mapperFactory.classMap(Person.class, PersonDTO.class) .field("age", "dtoAge") // Handling of different attribute names .byDefault() // Unlisted attributes are automatically matched .register(); Person person = new Person(); person.setAge("123"); // Strings and numbers can be converted to each other person.setName("Zhang San"); MapperFacade mapper = mapperFactory.getMapperFacade(); // The performance of MapperFacade is not as good as that of BoundMapperFacade PersonDTO personDTO = mapper.map(person, PersonDTO.class); System.out.println(personDTO); } } // Output PersonDTO(name = Zhang San, dtoAge=123, dtoBirth=null)
Simple usage 2:
import ma.glasnost.orika.BoundMapperFacade; import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.impl.DefaultMapperFactory; import org.example.entity.Person; import org.example.entity.PersonDTO; public class BoundMapperFacadeMain { public static void main(String[] args) { MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build(); mapperFactory.classMap(Person.class, PersonDTO.class) .field("age", "dtoAge") .byDefault() .register(); Person person = new Person(); person.setAge("456"); person.setName("Li Si"); BoundMapperFacade<Person, PersonDTO> boundMapper = mapperFactory.getMapperFacade(Person.class, PersonDTO.class); PersonDTO personDTO = boundMapper.map(person); System.out.println(personDTO); } } // Output PersonDTO(name = Li Si, dtoAge=456, dtoBirth=null)
Date to String example
Define converter:
import cn.hutool.core.date.DateTime; import ma.glasnost.orika.CustomConverter; import ma.glasnost.orika.MappingContext; import ma.glasnost.orika.metadata.Type; import java.util.Date; public class DateConverter extends CustomConverter<Date,String> { @Override public String convert(Date date, Type<? extends String> type, MappingContext mappingContext) { DateTime time = DateTime.of(date); return time.toString("yyyy-MM-dd HH:mm:ss"); } }
Main function:
import ma.glasnost.orika.BoundMapperFacade; import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.converter.ConverterFactory; import ma.glasnost.orika.impl.DefaultMapperFactory; import org.example.converter.DateConverter; import org.example.entity.Person; import org.example.entity.PersonDTO; import java.util.Date; public class MainB { public static void main(String[] args) { MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build(); ConverterFactory converterFactory = mapperFactory.getConverterFactory(); // Register converter converterFactory.registerConverter("DateConverterId", new DateConverter()); // Set the DateConverter id as DateConverterId here. If it is not set, it will be registered globally mapperFactory.classMap(Person.class, PersonDTO.class) .field("age", "dtoAge") .fieldMap("birth", "dtoBirth").converter("DateConverterId").add() .byDefault() .register(); Person person = new Person(); person.setAge("789"); person.setName("Wang Wu"); person.setBirth(new Date()); // Set Date BoundMapperFacade<Person, PersonDTO> boundMapper = mapperFactory.getMapperFacade(Person.class, PersonDTO.class); PersonDTO personDTO = boundMapper.map(person); System.out.println(personDTO); } } // Output PersonDTO(name = Wang Wu, dtoAge=789, dtoBirth=2021-11-29 20:34:21)
At this point, you can find that the customized converter has taken effect.
Summary
It is not difficult to find that in the actual project development, the MapperFactory above should be defined as a single example. A MapperFactory is shared globally. There are relevant instructions in the official documents. If you are interested, you can view the documents. The above is about Orika. Welcome to communicate and make common progress.
More usage
For more usage, please refer to the official documents:
Document address: http://orika-mapper.github.io/orika-docs/index.html
Github: https://github.com/orika-mapper?language=html
Note download
This article is original. Please attach a link for reprint.
This document provides markdown source file download. Please go to my code cloud warehouse for download. Download document
If this article is useful to you, please don't forget to give me a Star!