在開發中你可曾遇到以下這樣的問題?MyBtatis從數據庫中查詢的數據映射到domain的實體類上,而後有時候須要將domain的實體類映射給前端的VO類,用於展現。html
以下所示,假如Student是domain,而給前端展現的爲StudentVO。
前端
有沒有什麼優雅的解決方式呢?可能你的第一反應就是使用Spring的BeanUtils.copyProperties (),可是BeanUtils.copyProperties ()只能轉換類中字段名字同樣且類型同樣的字段。spring
因爲BeanUtils.copyProperties ()採用的是反射,實際上當重複調用時效率是比較低的。(實際測試實際測試Spring的BeanUtils在生成 次數爲1000000時須要1.6秒,而使用MapStruct僅須要69毫秒)。數據庫
首先導入Maven依賴安全
<dependency> <groupId>org.mapstruct</groupId> <!-- jdk8如下就使用mapstruct --> <artifactId>mapstruct-jdk8</artifactId> <version>1.2.0.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.2.0.Final</version> </dependency>
首先domain的Studnet類和StudentVO類以下,能夠看到字段是徹底一致的。其中@Data註解是lombok的表示含義以下,而@AllArgsConstructor則是提供全部參數的有參構造。微信
寫一個Mapper接口StudentMapper,此處的Mapper註解不是MyBtais的Mapper註解。app
接下來測試一下,看一下生成的結果。less
Studnet類的age和name與StudentVO類的ageVO和nameVO對應不上時dom
在Mapper類中加入@Mapping的註解指定原對象的字段名和要被對應上的字段名。其中@Mappings表示多個字段須要對應,若是隻是一個可使用@Mapping編輯器
接下來測試一下,看一下生成的結果。
某些時候,咱們的源不是一個,例如從數據庫中查詢出來了學生和老師,咱們須要將老師的名字給VO的name字段,學生的年齡給VO的age字段時可使用多參數源的映射方式。
在Mapper類的toStudentVO能夠看到帶了兩個參數,而後在@Mapping中使用形參的名字去點字段的名。
接下來測試一下,看一下生成的結果。
有些時候咱們須要多層映射,例如老師類中有本身的一個老婆類(男老師),而後咱們須要將老師類中的老婆類的名字,賦值給VO,而年齡則使用學生的年齡。聽上去怪怪的,就像學生有了老師的老婆😂 😂。
一樣能夠在Mapper類中使用符號"."的方式進行映射。
接下來測試一下,看一下生成的結果。
某些狀況下,你須要不建立目標類型的新實例,而是更新該類型的現有實例的映射。能夠經過爲目標對象添加參數並使用@MappingTarget標記此參數來實現此類映射。
例如Student咱們將學生類的名字和年齡映射到VO中,可是不建立新的實例。
在Mapper接口中使用@MappingTarget註解,被@MappingTarget註解標記的實例將從未被標記中進行的實例中進行映射。
接下來測試一下,看一下生成的結果。
前面咱們在Mapper接口中代碼中一直有一行代碼,以下所示,是MapStruct爲咱們提供的映射工廠,指定接口類型後自動幫咱們建立接口的實現,且保證是線程安全的單例,無需本身手動建立。
某些時候尤爲是在作項目時,咱們用到了Sping,但願映射後的新實例是交給Spring管理。這時候就須要進行依賴注入了。只須要在Mapper接口中的@Mapper註解中加入componentModel = "spring"便可。
映射屬性在源對象和目標對象中具備相同的類型,這種狀況不全有。例如,屬性在源bean中能夠是int類型,但在目標bean中能夠是Long類型。另外一個例子是對其餘對象的引用,這些對象應該映射到目標模型中的相應類型。例如:Teachr類可能有一個Wife類型的屬性wife,在映射VO對象時須要將其轉換爲StudentVO對象。
在許多狀況下,MapStruct會自動處理類型轉換。例如,若是屬性在源bean中的類型爲int,但在目標bean中的類型爲String,則生成的代碼將分別經過調用String.valueOf(int)和Integer.parseInt(String)來透明地執行轉換。
經過案例來實現從int轉換爲String 從BigDecimal到String的轉換 以及從Date到String的轉換
輸出結果以下所示
在映射集合的時候,咱們一樣能夠進行類型之間的轉換,以下所示使用@MapMapping註解指定輸出類型便可。
輸出結果以下所示
固然MapStruct也支持其餘各類類型的集合映射,上面只是舉例了Map的映射
MapStruct支持生成將一個Java枚舉類型映射到另外一個Java枚舉類型的方法。默認狀況下,源枚舉中的每一個常量都映射到目標枚舉類型中具備相同名稱的常量。若是須要,可使用@ValueMapping註解將源枚舉中的常量映射到具備其餘名稱的常量。源枚舉中的幾個常量能夠映射到目標類型中的相同常量。
Student中是SexEnum枚舉,而StudentVO中是Sex2Enum,且枚舉中的值是一致時,咱們須要將Student中的映射到StudentVO中,此時只須要使用@Mapping來指定映射源和目標源的名稱便可
當枚舉值同樣時,直接使用@Mapping來指定映射源和目標源的名稱便可
當枚舉值不一致時,使用@ValueMapping註解。
使用@ValueMapping註解,同時因爲Student和StudentVO中的枚舉類型不一致,因此以前的@Mapping註解也要使用。
有時候因爲目標實例的構造方法被私有化後,咱們使用原來的方式沒辦法進行,緣由是MapStruct會在編譯時去幫你實現,其中包含了調用構造方法。因此咱們能夠定義工廠的形式來生成實例,而讓MapStruct去調用工廠來生成實例,而再也不使用構造方法。
有咱們私有化了StudentVO的構造方法,若是直接使用MapStruct進行映射是會報錯的。
指定工廠,同時在Mapper接口中的@Mapper註解上加入工廠的class
輸出以下
在某些狀況下,可能須要定製生成的映射方法,在目標對象中設置一個沒法由MapStruct生成的方法實現時,可使用自定義映射來完成。假如咱們的StudentVO中的age是沒法生成的。
首先定義類,而後實現Mapper接口,在重寫的方法中寫上須要的邏輯,且在Mapper接口中加入@DecorateWith註解,指定自定義映射的class。
測試輸出結果,能夠看到先給age值爲0,最後輸出爲100.
上面的MapStruct只寫了一些經常使用的,以及我以爲可能會用到的,其中MapStruct還包含不少種用法,若是你想徹底的瞭解他的全部功能,能夠參考MapStruct的官方文檔,文檔地址能夠在最下面能夠看到。
文檔地址:
http://mapstruct.org/documentation/stable/reference/html/
本文分享自微信公衆號 - 大貓的Java筆記(damaoJava)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。