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 哦!