Orika
前言
類復制工具有很多,比較常用的有 mapstruct、Spring BeanUtils、Apache BeanUtils、dozer 等,目前我所在的項目組中使用的是 mapstruct。在性能方面,mapstruct 毫無疑問是最優秀的,因為 mapstruct 是通過 getter、setter 方法來復制屬性值的,而其它框架或多或少使用反射進行復制,這里也不再贅述。但是,mapstruct 也有它的不足之處,請看下面:
不知道大家使用 mapstruct 時,是否編寫過類似如下的 java 表達式:
@Mapper
public interface SmsTemplateConverter {
SmsTemplateConverter SMS_TEMPLATE_CONVERTER = Mappers.getMapper(SmsTemplateConverter.class);
@Mappings({
// 這里只能通過全類名來調用靜態方法,否則類無法注入到編譯后的文件
@Mapping(target = "templateType", expression = "java(org.example.enums.SmsEnum.getTypeByCode(platformTemp.getTemplateType()))")
})
SmsCompanyTemplateVO toSmsCompanyTemplateVO(SmsCompanyTemplate companyTemp, SmsPlatformTemplate platformTemp);
}
我們不難發現,一旦這里的 org.example.enums.SmsEnum
全類名目錄發生改變,此處的代碼就會報錯,因為這里的 expression 是字符串,在目錄更改時,不能自動更改全類名路徑(因為是字符串,不是真正的 java 代碼,mapstruct 的 java 表達式是由代碼生成器生成的,在編譯后 target 目錄下可以看到),等於是寫死的,后期維護和擴展時會比較困難,因此我們項目中決定放棄 mapsruct。
在調研了眾多類復制工具后,我選擇了 Orika,並通過 demo 驗證確實可行,在了解 Orika 前,不妨了解一下各個類復制工具的對比,如下圖示:(圖片源於網絡,如有侵權,請聯系刪除)
使用示例
下面我會以一個基本示例和一個Date屬性轉String屬性的示例來示范 Orika 的使用。
導入依賴
<!-- 類復制工具:orika -->
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.2</version><!-- or latest version -->
</dependency>
<!-- hu-tool 工具包 -->
<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>
定義實體 Person 和 PersonDTO
Person實體:
import lombok.Data;
import java.util.Date;
@Data
public class Person {
private String name; // 注意這個字段名是與 PersonDTO 相同的
private String age;
private Date birth;
}
PersonDTO實體:
import lombok.Data;
@Data
public class PersonDTO {
private String name;
private Integer dtoAge;
private String dtoBirth;
}
基本示例
簡單用法一:
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") // 屬性名不同時的處理
.byDefault() // 未列舉的屬性自動匹配
.register();
Person person = new Person();
person.setAge("123"); // 字符串與數字可以互轉
person.setName("張三");
MapperFacade mapper = mapperFactory.getMapperFacade(); // MapperFacade 的性能不如 BoundMapperFacade
PersonDTO personDTO = mapper.map(person, PersonDTO.class);
System.out.println(personDTO);
}
}
// 輸出 PersonDTO(name=張三, dtoAge=123, dtoBirth=null)
簡單用法二:
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("李四");
BoundMapperFacade<Person, PersonDTO> boundMapper =
mapperFactory.getMapperFacade(Person.class, PersonDTO.class);
PersonDTO personDTO = boundMapper.map(person);
System.out.println(personDTO);
}
}
// 輸出 PersonDTO(name=李四, dtoAge=456, dtoBirth=null)
Date 轉 String 示例
定義 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");
}
}
主函數:
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(); // 注冊 converter
converterFactory.registerConverter("DateConverterId", new DateConverter()); // 這里給 DateConverter 設置一個 id 為 DateConverterId,如果不設置,則為全局注冊
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("王五");
person.setBirth(new Date()); // 設置 Date
BoundMapperFacade<Person, PersonDTO> boundMapper =
mapperFactory.getMapperFacade(Person.class, PersonDTO.class);
PersonDTO personDTO = boundMapper.map(person);
System.out.println(personDTO);
}
}
// 輸出 PersonDTO(name=王五, dtoAge=789, dtoBirth=2021-11-29 20:34:21)
此時可以發現,自定義的轉換器已經生效。
小結
不難發現,上面的 MapperFactory 在實際的項目開發中,應該定義為單例,由全局來共享一個 MapperFactory,官方文檔中也有相關說明,感興趣可以查看文檔,以上就是有關 Orika 的分享,歡迎交流,共同進步。
更多用法
更多用法請參考官方文檔:
文檔地址:http://orika-mapper.github.io/orika-docs/index.html
Github:https://github.com/orika-mapper?language=html
筆記下載
此文章系原創,轉載請附上鏈接,抱拳。
此文檔提供 markdown 源文件下載,請去我的碼雲倉庫進行下載。 下載文檔
若本文對你有用,請不要忘記給我的點個 Star 哦!