性能爆棚的實(shí)體轉(zhuǎn)換復(fù)制工具M(jìn)apStruct使用詳解
引言
Java項(xiàng)目中實(shí)體轉(zhuǎn)換無(wú)處不在,當(dāng)實(shí)體字段較多或者大批量的進(jìn)行復(fù)制時(shí),通過(guò)手工setter/getter顯得太LOW,同時(shí)兼?zhèn)涓咝阅芤笄闆r下,MapStruct完全完全能夠勝任。官方解釋,MapStruct是一個(gè)代碼生成器,它基于約定優(yōu)于配置的方法,極大地簡(jiǎn)化了Java bean類型之間映射的實(shí)現(xiàn)。生成的映射代碼使用普通方法調(diào)用,因此快速、類型安全且易于理解。因?yàn)镸apStruct是在編譯期間生成setter/getter方法,實(shí)際運(yùn)行時(shí)就是直接調(diào)用setter/getter,效率會(huì)非常高。
優(yōu)點(diǎn)
- MapStruct編譯期生成映射代碼,所以可以在編譯時(shí)暴露映射錯(cuò)誤的代碼,讓錯(cuò)誤提前暴露;
- 因?yàn)槭褂胹etter/getter方式,而非反射方式,所以可以更快的執(zhí)行效率;
- 可以實(shí)現(xiàn)深拷貝,自動(dòng)類型轉(zhuǎn)換,如枚舉轉(zhuǎn)換;
- 進(jìn)行自定義的映射,多種映射方式,下邊具體說(shuō)明;
性能對(duì)比
對(duì)比對(duì)象 | 10個(gè)對(duì)象復(fù)制1次 | 1萬(wàn)個(gè)對(duì)象復(fù)制1次 | 100萬(wàn)個(gè)對(duì)象復(fù)制1次 | 100萬(wàn)個(gè)對(duì)象復(fù)制5次 |
---|---|---|---|---|
MapStruct | 0ms | 3ms | 96ms | 281ms |
Hutools的BeanUtil | 23ms | 102ms | 1734ms | 8316ms |
Spring的BeanUtils | 2ms | 47ms | 726ms | 3676ms |
Apache的BeanUtils | 20ms | 156ms | 10658ms | 52355ms |
Apache的PropertyUtils | 5ms | 68ms | 6767ms | 30694ms |
使用
依賴
<!-- MapStruct核心,包含了一些必要的注解--> <dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <annotationProcessorPaths> <!-- MapStruct編譯,注解處理器,根據(jù)注解自動(dòng)生成Mapper的實(shí)現(xiàn) --> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
定義轉(zhuǎn)換接口
/** * 測(cè)試接口 * * @author reboot */ @Mapper public interface OrderConvertor { /** * 實(shí)例 */ OrderConvertor INSTANCE = Mappers.getMapper(OrderConvertor.class); /** * OrderDo -> OrderModel * * @param orderDo 訂單實(shí)體 * @return {@link OrderModel} */ OrderModel toModel(OrderDo orderDo); /** * OrderDo -> OrderModel * * @param orderDos 訂單實(shí)體 * @return {@link OrderModel} */ List<OrderModel> toModel(List<OrderDo> orderDos); /** * OrderModel -> OrderDo * * @param orderModel 訂單模型 * @return {@link OrderDo} */ OrderDo toDo(OrderModel orderModel); /** * OrderModel -> OrderDo * * @param orderModels 訂單模型 * @return {@link OrderDo} */ List<OrderDo> toDo(List<OrderModel> orderModels); }
編譯結(jié)果
MapStruct會(huì)自動(dòng)生成對(duì)應(yīng)接口的實(shí)現(xiàn),并自動(dòng)完成屬性映射關(guān)系,List會(huì)自動(dòng)進(jìn)行批量處理。
調(diào)用
/** * 訂單服務(wù) * * @author reboot */ @Service public class OrderService { /** * 獲取訂單列表 * * @return {@link List}<{@link OrderModel}> */ public List<OrderModel> getOrderList() { // 獲取數(shù)據(jù)庫(kù)數(shù)據(jù)DO List<OrderDo> result = selectOrderList(); // 參數(shù)轉(zhuǎn)換 return OrderConvertor.INSTANCE.toModel(result); } }
插件
上邊的使用方式雖然能夠正常使用,但是在一些屬性配置映射上和提示上,如果使用插件能夠提升使用體驗(yàn),IDEA中可以直接安裝Mapstruct Support插件,當(dāng)然Eclipse也有對(duì)應(yīng)的插件。
特性
- 突出顯示目標(biāo)屬性和源屬性。將目標(biāo)屬性和源屬性轉(zhuǎn)到聲明的setter / getter中;
- 錯(cuò)誤和快速修復(fù):
- 缺少@Mapper或@MapperConfig注解檢查;
- 快速修復(fù)未映射的目標(biāo)屬性,添加未映射目標(biāo)屬性和忽略未映射目標(biāo)屬性;
其他用法
更加詳細(xì)的內(nèi)容可以查看官方文檔,發(fā)布文章時(shí)最新版本是 MapStruct 1.5.3.Final.html。
基礎(chǔ)映射
@Mapper public interface CarMapper { @Mapping(target = "manufacturer", source = "make") @Mapping(target = "seatCount", source = "numberOfSeats") CarDto carToCarDto(Car car); @Mapping(target = "fullName", source = "name") PersonDto personToPersonDto(Person person); }
target表示目標(biāo)屬性名,source表示源屬性名,一般在目標(biāo)屬性和源屬性不同時(shí)使用,相同的屬性名會(huì)自動(dòng)進(jìn)行映射。
映射器添加自定義方法
@Mapper public interface CarMapper { @Mapping(...) ... CarDto carToCarDto(Car car); default PersonDto personToPersonDto(Person person) { //hand-written mapping logic } }
自定義方法personToPersonDto并實(shí)現(xiàn),在生成的實(shí)現(xiàn)類中會(huì)進(jìn)行覆蓋使用。
多個(gè)源參數(shù)映射
@Mapper public interface AddressMapper { @Mapping(target = "description", source = "person.description") @Mapping(target = "houseNumber", source = "address.houseNo") DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address); @Mapping(target = "description", source = "person.description") @Mapping(target = "houseNumber", source = "hn") DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn); }
存在多個(gè)源參數(shù),使用參數(shù)名.屬性名的方式進(jìn)行表示,也可以直接使用基礎(chǔ)類型的屬性名稱。
嵌套屬性映射到當(dāng)前目標(biāo)
@Mapper public interface CustomerMapper { @Mapping( target = "name", source = "record.name" ) @Mapping( target = ".", source = "record" ) @Mapping( target = ".", source = "account" ) Customer customerDtoToCustomer(CustomerDto customerDto); }
當(dāng)源參數(shù)中存在對(duì)象屬性,可以手動(dòng)進(jìn)行映射,或者直接使用"."的方式將對(duì)象中的屬性全部映射到當(dāng)前目標(biāo)對(duì)象。
表達(dá)式方式
@Mapper public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mapping( target = "timeAndFormat", expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )" ) Target sourceToTarget(Source s); }
支持使用java代碼塊進(jìn)行轉(zhuǎn)換,一般可以將靜態(tài)方法處理的字段放到這里。
更新現(xiàn)有實(shí)例
@Mapper public interface CarMapper { void updateCarFromDto(CarDto carDto, @MappingTarget Car car); }
@MappingTarget源參數(shù),編譯時(shí)會(huì)將carDto參數(shù)中的屬性映射到car參數(shù)中。
Map映射
@Mapper public interface CustomerMapper { @Mapping(target = "name", source = "customerName") Customer toCustomer(Map<String, String> map); }
直接將map中的key進(jìn)行映射。
更多用法
還有更多其他用法,比如:
- 支持映射定義的public屬性;
- 支持映射參數(shù)Builder模式;
- 使用注入方式引入轉(zhuǎn)換器;
- 數(shù)據(jù)類型字段轉(zhuǎn)換,如枚舉、日期,支持日期格式化,支持?jǐn)?shù)字類型格式化,具體可以看 Implicit type conversions;
- 集合類型自動(dòng)轉(zhuǎn)換;
- 轉(zhuǎn)換Stream;
- ......
總結(jié)
MapStruct還有很多其他高階特性,限于篇幅文章僅僅列舉部分示例,有興趣的同學(xué)可以查看對(duì)應(yīng)文檔試試。使用適當(dāng)?shù)墓ぞ哂行岣呔幊绦?,在使用工具過(guò)程中我們也了解其實(shí)現(xiàn)原理,不斷提高自身。后邊有時(shí)間也把MapStruct實(shí)現(xiàn)原理拿出來(lái)講講,跟大家一起學(xué)習(xí)進(jìn)步!
以上就是性能爆棚的實(shí)體轉(zhuǎn)換復(fù)制工具M(jìn)apStruct使用詳解的詳細(xì)內(nèi)容,更多關(guān)于MapStruct實(shí)體轉(zhuǎn)換復(fù)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java利用 Exchanger 實(shí)現(xiàn)游戲中交換裝備
JDK 1.5 開(kāi)始 JUC 包下提供的 Exchanger 類可用于兩個(gè)線程之間交換信息。下面我們就來(lái)看看Java是如何利用Exchanger一行代碼實(shí)現(xiàn)游戲中交換裝備的2021-09-09java常用工具類 XML工具類、數(shù)據(jù)驗(yàn)證工具類
這篇文章主要為大家詳細(xì)介紹了java常用工具類,包括XML工具類、數(shù)據(jù)驗(yàn)證工具類,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05Springcloud服務(wù)注冊(cè)consul客戶端過(guò)程解析
這篇文章主要介紹了Springcloud服務(wù)注冊(cè)consul客戶端過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08SpringBoot升級(jí)3.2報(bào)錯(cuò)Invalid value type for
這篇文章給大家介紹了SpringBoot升級(jí)3.2報(bào)錯(cuò)Invalid value type for attribute ‘factoryBeanObjectType‘: java.lang.String的解決方案,文中有詳細(xì)的原因分析,需要的朋友可以參考下2023-12-12java利用socket通信實(shí)現(xiàn)Modbus-RTU通信協(xié)議的示例代碼
這篇文章主要介紹了java利用socket通信實(shí)現(xiàn)Modbus-RTU通信協(xié)議的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04String類型傳遞是值傳遞,char[]類型傳遞是引用傳遞的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇String類型傳遞是值傳遞,char[]類型傳遞是引用傳遞的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看不2016-09-09Java實(shí)現(xiàn)計(jì)算一個(gè)月有多少天和多少周
這篇文章主要介紹了Java實(shí)現(xiàn)計(jì)算一個(gè)月有多少天和多少周,本文直接給出實(shí)例代碼,需要的朋友可以參考下2015-06-06