日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Mapstruct详细使用说明

發布時間:2023/12/8 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mapstruct详细使用说明 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在使用分層或者分模塊化的項目中,我們可能定義各種各樣的O,例如:DO,VO,DTO等等。我們在進行這些對象之間的拷貝時,通過手動寫get/set方法進行屬性之間的賦值。因為他們之間的屬性大部分都是相同的,不僅浪費時間,并且還有大量重復代碼。所以,各種框架都添加的對象之間的拷貝的工具類。例如:

Spring自帶了BeanUtils

Apatch自帶的BeanUtils

Apatch自帶的PropertyUtils

mapstruct提供Mappers

本文主要介紹MapStruct的基礎知識、Mapstruct的優缺點、Mapctruct的拷貝示例、以及四種方法時間對比。

Mapstuct介紹

1.mapstruct的官網地址:
https://mapstruct.org

2.mapstruct的文檔地址:
https://mapstruct.org/documentation/stable/reference/html/

3.mapstrcut的示例github地址
https://github.com/mapstruct/mapstruct-examples

4.mapstruct的介紹
mapstruct的作用:就像mapstruct官網所說的:mapsrtuct是一個用于簡化在JavaBean之間映射的代碼生成器工具,并且是基于約定高于配置的方式。

因為生成的代碼使用簡單的get/set方法,所以mapstruct可以更快執行、類型之間復制更安全、且容易理解。

mapstruct的優缺點

下面看看mapstruct相比其他映射工具有什么優點

1.mapstruct的優點:
mapstruct是在代碼編譯的時候,生成其映射規則的代碼,所以會在編譯時暴露映射錯誤的代碼,讓錯誤提前暴露。

因為使用的是一些簡單方法:set/get或者Fluent方式,而非反射方式,所以可以更快的執行完成??梢酝ㄟ^04章節查看各個方法效率上對比

可以實現深拷貝,上面4種方法中只有mapstruct可以實現深拷貝。但是需要進行配置(使用@mapper(mappingControl=DeepClone.class)進行設置)。其他的都是淺拷貝。從而mapstruct實現修改新對象不會對老對象產生影響。

類型更加安全

可以進行自定義的映射??梢宰远x各種方法,完成屬性映射。例如:將兩個屬性數據合并成一個設置到目標屬性上

2.mapstruct的缺點
必須添加一個接口或者抽象類,才能實現映射。其他的三種方法都不用寫額外接口或者類。

Mapstruct的簡單示例使用

上面主要是介紹一下Mapstruct的基礎知識,下面看一個mapstruct的簡單示例,完成屬性之間的映射。

在示例中,我們使用mapstruct的1.4.2.Final版本,進行試驗的1.第一個原屬性數據 //第一個拷貝對象 public class CompareA {private String name;private Integer age;private LocalDateTime createTime;private double score;private Double totalScore;private Date updateTIme;private ChildCompare childCompare;private Long days;//省略其get/set方法,也可使用Lombok,以后講解lombok與mapstruct結合使用}
  • 第二個目標屬性數據
  • public class CompareB {private String name;private Integer age;private String createTime;private double score;private Double totalScore;private Date updateTIme;private ChildCompare childCompare;private Long day;//省略其get/set方法, }

    3.子類

    //這是CompareA和CompareB共有的對象 public class ChildCompare {private String childName;private long childAge;//省略其get/set方法, }

    上面是兩個實體對象,觀察這兩個實體可以看到有兩點不同:

    CompareA中有一個createTime屬性,數據類型為LocalDateTime,CompareB也有一個createTime,但是屬性類型為String
    CompareA有一個days屬性,而CompareB有一個day屬性

    4.mapstruct接口(這是需要我們手動寫的)

    @Mapper public interface CompareMapper {CompareMapper INSTANCE = Mappers.getMapper(CompareMapper.class);@Mapping(target = "day", source = "days")@Mapping(target = "createTime", source = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")@Mapping(target = "childCompare", source = "childCompare", mappingControl = DeepClone.class)CompareB mapper(CompareA compareA); }

    從上面的實現中,我們加入了三個注解:

    第一個注解:完成屬性名不匹配的問題,將compareA中的days(source)賦值到compareB的day(target)
    第二個注解:完成屬性類型不匹配的問題,將compareA中的createTime,賦值到compareB的createTime,這里進行日期的格式轉化,使用dateFormat設置格式化類型
    第三個注解:完成深度拷貝,將compareA中的childCompare屬性進行深度拷貝,而不是指針復制,最重要的是最后一個參數:mappingControl = DeepClone.class

    5.最后就會自動生成一個實現類:

    @Generated(value = "org.mapstruct.ap.MappingProcessor" ) public class CompareMapperImpl implements CompareMapper {@Overridepublic CompareB mapper(CompareA compareA) {if ( compareA == null ) {return null;}CompareB compareB = new CompareB();compareB.setDay( compareA.getDays() );if ( compareA.getCreateTime() != null ) {compareB.setCreateTime( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ).format( compareA.getCreateTime() ) );}compareB.setChildCompare( childCompareToChildCompare( compareA.getChildCompare() ) );compareB.setName( compareA.getName() );compareB.setAge( compareA.getAge() );compareB.setScore( compareA.getScore() );compareB.setTotalScore( compareA.getTotalScore() );compareB.setUpdateTIme( compareA.getUpdateTIme() );return compareB;}protected ChildCompare childCompareToChildCompare(ChildCompare childCompare) {if ( childCompare == null ) {return null;}ChildCompare childCompare1 = new ChildCompare();childCompare1.setChildName( childCompare.getChildName() );childCompare1.setChildAge( childCompare.getChildAge() );return childCompare1;} }

    從上面的實現,我們可以看出,添加一個childCompareToChildCompare方法,來完成對象之間的深度拷貝。同樣的將days設置到了day屬性,等等。

    6.最后,我們調用這個mapper方法,就可以完成對象之間的拷貝:如下:

    CompareMapper.INSTANCE.mapper(new CompareA());

    對象映射之間的比較

    public class CompareTest {int count = 1000000;/*** Spring中的BeanUtils的使用,最后使用了6700ms*/@Testpublic void springBeanUtils() {CompareA populate = populate();StopWatch stopWatch = new StopWatch();stopWatch.start("開始拷貝");for (int i = 0; i < count; i++) {CompareB compareB = new CompareB();BeanUtils.copyProperties(populate, compareB);}stopWatch.stop();System.out.println(stopWatch.getTotalTimeMillis());}/*** 這是使用mapstruct進行對象拷貝,使用時間54ms*/@Testpublic void mapstructBeanUtils() {CompareA populate = populate();StopWatch stopWatch = new StopWatch();stopWatch.start("開始拷貝");for (int i = 0; i < count; i++) {CompareB mapper = CompareMapper.INSTANCE.mapper(populate);}stopWatch.stop();System.out.println(stopWatch.getTotalTimeMillis());}/*** 這是Apache的BeanUtils工具,使用7086ms* @throws InvocationTargetException* @throws IllegalAccessException*/@Testpublic void commonBeanUtils() throws InvocationTargetException, IllegalAccessException {CompareA populate = populate();StopWatch stopWatch = new StopWatch();stopWatch.start("開始拷貝");for (int i = 0; i < count; i++) {CompareB compareB = new CompareB();org.apache.commons.beanutils.BeanUtils.copyProperties(compareB, populate);}stopWatch.stop();System.out.println(stopWatch.getTotalTimeMillis());}/*** 使用Apache中的propertyUtils靜態方法,使用3756ms* @throws IllegalAccessException* @throws NoSuchMethodException* @throws InvocationTargetException*/@Testpublic void commonPropertiesUtils() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {CompareA populate = populate();StopWatch stopWatch = new StopWatch();stopWatch.start("開始拷貝");for (int i = 0; i < count; i++) {CompareB compareB = new CompareB();PropertyUtils.copyProperties(compareB, populate);}stopWatch.stop();System.out.println(stopWatch.getTotalTimeMillis());}public CompareA populate() {CompareA compareA = new CompareA();compareA.setAge(10);compareA.setName("moxiao");compareA.setCreateTime(LocalDateTime.now());compareA.setDays(100L);compareA.setTotalScore(100.0);compareA.setScore(12.3);compareA.setUpdateTIme(new Date());ChildCompare childCompare = new ChildCompare();childCompare.setChildAge(200);childCompare.setChildName("moxiaoChild");compareA.setChildCompare(childCompare);return compareA;}}

    綜上所述,1百萬數據,mapstruct可以在50ms左右完成。

    Mapstruct的屬性之間的映射規則

    在屬性映射方面,mapstruct相比于其他映射工具有:

    • 可以進行不同屬性名之間的映射,使用@Mapping注解的source和target
    • 可以使用表達式進行設置,使用@Mapping注解中的expression
    • 可以設置默認值,使用@Mapping注解中的constant,
    • 可以進行不同類型之間的映射,并且更加安全
    • 可以進行自定義映射方法

    mapstruct的映射控制說明

    2.1 映射控制的作用:

    在源對象和目標對象之間進行映射時,哪種映射方法將被考慮。即:只有在有這個控制條件下,才會執行對應的映射方法。所有屬性之間的復制或者拷貝都將是依據以下映射控制來完成的。只有更好地理解這四種映射控制,才能按照我們的想法進行對象之間的拷貝。

    2.2 映射控制的種類:

    mapstruct一共有四種映射控制:

    MappingControl.Use.DIRECT
    直接映射:直接將源屬性賦值到目標屬性上,如果是對象的話,就是指針復制,也就是淺拷貝。這種方法有一個前提:目標屬性是源屬性的父類或者相同類型。

    MappingControl.Use.BUILT_IN_CONVERSION
    內置映射:將使用mapstruct內置的映射方法。有哪些內置映射方法,例如:日期和字符串、原生類型和對應的包裝對象、字符串和枚舉類型等等。

    MappingControl.Use.MAPPING_METHOD
    映射方法:包含兩類:可以是自定義的映射方法,也可以是系統自動生成的映射方法。注意:自定義映射方法比系統自動生成的映射方法的優先級更高。例如:深度拷貝就是使用這個映射控制。系統自動生成的映射方法:一般都是對象之間的映射。

    MappingControl.Use.COMPLEX_MAPPING
    復合映射:是一種結合BUILT_IN_CONVERSION和MAPPING_METHOD的映射控制,來完成對象屬性之間的拷貝。當進行COMPLEX_MAPPING時:一共有三種形態:

    a) target = method1( method2( source ) )b) target = method( conversion( source ) )c) target = conversion( method( source ) )其中method開頭表示:自定義映射方法,conversion開頭的表示:內置映射方法。注意:復合映射中只能進行兩次轉化,不能多于兩次,也不能少于兩次。這三種形態不含兩個內置映射方法來完成屬性之間的拷貝。即:至少有一個自定義的映射方法的存在。注意:在沒有某一中映射控制時,就不會采用這種方式完成屬性映射,例如:沒有MAPPING_METHOD就會不采用自定義的映射方法完成屬性映射。

    2.3 映射控組組合

    四種映射控制可以組合使用,即同時使用兩個及以上的映射控制,從而在這些映射控制中選擇一種方式使用。那這就涉及到映射控制之間優先級。

    2.4 mapstruct內置的映射控制組合

    在mapstruct中一共有三種內置的映射控制組合,以及自定義的映射組合。

    @MappingControl注解類

    該注解使用了全部的映射控制:DIRECT、MAPPING_METHOD、 BUILT_IN_CONVERSION、COMPLEX_MAPPING。這也這是:默認使用映射控制。

    @DeepClone注解類

    該注解只使用了:MAPPING_METHOD映射,當我們使用這個注解時,將不會采用其他的三種方式來完成屬性之間的拷貝。

    @NoComplexMapping注解類

    改注解使用了三種:DIRECT、MAPPING_METHOD、 BUILT_IN_CONVERSION,即將不會使用復合映射方法完成屬性之間的拷貝

    自定義的映射組合
    我們可以聲明一個注解類,自定義只是用哪些映射控制,例如:只使用內置的映射方法

    @Retention(value = RetentionPolicy.CLASS)
    @MappingControl( MappingControl.Use.BUILT_IN_CONVERSION )
    public @interface BuildInConversionControl {
    }

    2.5 映射控制的使用

    mapstruct默認使用的是:@MappingControl注解類,即四種映射控制我們可以在@Mapper注解中的mappingControl屬性中設置,他的屬性值是一個注解類。在@Mapper注解中設置這個屬性:將會應用到這個類中的所有方法的屬性之間的拷貝我們也可以在@Mapping注解中的mappingControl屬性中設置。在@Mapping注解中設置這個屬性:將只會應用到指定屬性上。@Mapping的優先級比@Mapper的高,即優先采用@Mapping,如果沒有才會使用@Mapper進行屬性之間的映射。

    mapstruct的映射控制優先級

    在有多個映射控制使用時,例如在使用:@MappingControl注解,mapstruct將會如何進行屬性之間的映射。這里就涉及到四種映射控制之間的優先級。

    3.1 四種映射控制的優先級,從高到低

    在有MAPPING_METHOD控制下,并且包含有一個自定義的映射方法。這個自定義方法必須滿足:輸入參數為源屬性父級或者相同類型,這個映射方法的返回值為目標屬性的子類或相同類型。必須都滿足這兩個條件:這樣在進行屬性拷貝時,才會使用自定義的映射方法。不然則跳過。
    在有DIRECT控制下,并且目標屬性必須是源屬性的父類或者相同類型。功能:直接將源屬性的指針拷貝到目標屬性下。必須滿足這兩個條件,才會使用指針拷貝,不然則跳過。
    如果有BUILT_IN_CONVERSION的控制下,在源屬性和目標屬性之間拷貝時,mapstruct就會根據源屬性類型和目標屬性類型找到對應的內置映射方法使用,沒有找到,就跳過。
    如果有COMPLEX_MAPPING控制下,它又劃分成三種類型:
    如果存在兩個自定義的方法,可以將源屬性轉化為目標屬性,即: target = method1( method2( source ) ),優先使用。

    如果有一個自定義方法,一個內置映射方法,可以將源屬性轉化為目標屬性,就會使用其中一個target = method( conversion( source ) )或者target = conversion( method( source ) )。5. 如果有MAPPING_METHOD控制下,將自動生成一個映射方法,完成屬性之間的映射。必須對象,屬性不能是:8中基本類型和對應的包裝類以及String

    3.2 注意事項:

    如果源屬性是8種基本類型或者包裝體,而目標屬性是對應的類型(不能是不對應的),則會直接拷貝。就算有就不會走上面的優先級。例如:源屬性為long,目標屬性為Long,不管配置的什么,都會直接賦值。

    mapstruct的映射控制的示例

    public class MappingOrder {private String name;private Integer age;private LocalDateTime birthTime;private MapperOrderChild mapperOrderChild;private MappingOrderChildChild mapperOrderChild2;//省略其set/get方法}

    目標對象

    public class MappingOrderVO {private String name;private Number age;private String birthString;private MapperOrderChild mapperOrderChild;private MapperOrderChild2 mapperOrderChild2;}

    映射類(必須自己單獨定義一個,mapstruct必須提供一個mapper接口或者抽象類)

    @Mapper public interface MapperOrderMapper {@Mapping(source = "birthTime", target = "birthString", dateFormat = "yyyy-MM-dd HH:mm:ss")//這一行進行深度拷貝,注意最后一個參數很重要:mappingControl = DeepClone.class@Mapping(source = "mapperOrderChild2", target = "mapperOrderChild2", mappingControl = DeepClone.class)MappingOrderVO entityToVo(MappingOrder mappingOrder);}

    mapstruct自動生成的文件

    public class MapperOrderMapperImpl implements MapperOrderMapper {@Overridepublic MappingOrderVO entityToVo(MappingOrder mappingOrder) {if ( mappingOrder == null ) {return null;}MappingOrderVO mappingOrderVO = new MappingOrderVO();//第一個屬性,因為沒有自定義方法從LocalDateTime轉化為String,將跳過,//不能直接映射,優先級第二條//mapstruct有一個從LocalDateTime轉化為String的內置方法,從而滿足第三條映射if ( mappingOrder.getBirthTime() != null ) {mappingOrderVO.setBirthString( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ).format( mappingOrder.getBirthTime() ) );}//我們在方法中使用DEEPCLONE,即MAPPING_METHOD控制//所以只有第一條優先級和第五條優先級,滿足。因為沒有自定義映射方法,從而跳過//所以Mapstruct使用第五條,自動生成一個映射方法mappingOrderVO.setMapperOrderChild2( mappingOrderChildChildToMapperOrderChild2( mappingOrder.getMapperOrderChild2() ) );//DIRECT優先級獲勝mappingOrderVO.setName( mappingOrder.getName() );//DIRECT優先級獲勝mappingOrderVO.setAge( mappingOrder.getAge() );//DIRECT優先級獲勝mappingOrderVO.setMapperOrderChild( mappingOrder.getMapperOrderChild() );return mappingOrderVO;}protected MapperOrderChild2 mappingOrderChildChildToMapperOrderChild2(MappingOrderChildChild mappingOrderChildChild) {if ( mappingOrderChildChild == null ) {return null;}MapperOrderChild2 mapperOrderChild2 = new MapperOrderChild2();mapperOrderChild2.setFirstName( mappingOrderChildChild.getFirstName() );mapperOrderChild2.setSecondName( mappingOrderChildChild.getSecondName() );return mapperOrderChild2;} }

    總結

    以上是生活随笔為你收集整理的Mapstruct详细使用说明的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。