Java 實體映射工具 MapStruct

一、名詞解釋

  • DTO(Data Transfer Object):數(shù)據(jù)傳輸對象,這個概念來源于J2EE的設(shè)計模式,原來的目的是為了EJB的分布式應(yīng)用提供粗粒度的數(shù)據(jù)實體宜岛,以減少分布式調(diào)用的次數(shù),從而提高分布式調(diào)用的性能和降低網(wǎng)絡(luò)負載功舀,但在這里,我泛指用于展示層與服務(wù)層之間的數(shù)據(jù)傳輸對象身弊。
  • DO(Domain Object):領(lǐng)域?qū)ο蟊偬褪菑默F(xiàn)實世界中抽象出來的有形或無形的業(yè)務(wù)實體。

說明:在一個成熟的工程中阱佛,尤其是現(xiàn)在的分布式系統(tǒng)中帖汞,應(yīng)用與應(yīng)用之間,還有單獨的應(yīng)用細分模塊之后凑术,DO 一般不會讓外部依賴翩蘸,這時候需要在提供對外接口的模塊里放 DTO 用于對象傳輸,也即是 DO 對象對內(nèi)淮逊,DTO對象對外催首,DTO 可以根據(jù)業(yè)務(wù)需要變更(只需部分字段或字段名不同等)扶踊,并不需要映射 DO 的全部屬性。

這種 對象與對象之間的互相轉(zhuǎn)換郎任,就需要有一個專門用來解決轉(zhuǎn)換問題的工具秧耗,畢竟每一個字段都 get/set 會很麻煩。

MapStruct 就是這樣的一個屬性映射工具舶治,只需要定義一個 Mapper 接口分井,MapStruct 就會自動實現(xiàn)這個映射接口,避免了復(fù)雜繁瑣的映射實現(xiàn)霉猛。MapStruct官網(wǎng)地址: http://mapstruct.org/

二尺锚、maven配置

<properties>
    <mapstruct.version>1.2.0.Final</mapstruct.version>
</properties>

<dependencies>
    <dependency>
      <groupId>org.mapstruct</groupId>
      <artifactId>mapstruct-jdk8</artifactId>
      <version>${mapstruct.version}</version>
    </dependency>
    <dependency>
      <groupId>org.mapstruct</groupId>
      <artifactId>mapstruct-processor</artifactId>
      <version>${mapstruct.version}</version>
    </dependency>
</dependencies>

三、基本用法 一對一 DO TO DTO

  • 兩個 DO 對象 Person 和 User惜浅,其中 user 是 Person 的一個屬性 瘫辩,一個 DTO 對象 PersonDTO
  • DO中的使用Lombok省略get/set/構(gòu)造方法中
@NoArgsConstructor  
@AllArgsConstructor
@Data
public class Person {
    private Long id;
    private String name;
    private String email;
    private Date birthday;
    private User user;
}

@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
    private Integer age;
}

@NoArgsConstructor
@AllArgsConstructor
@Data
public class PersonDTO {
    private Long id;
    private String name;
    /**
     * 對應(yīng) Person.user.age
     */
    private Integer age;
    private String email;
    /**
     * 與 DO 里面的字段名稱(birthDay)不一致
     */
    private Date birth;
    /**
     * 對 DO 里面的字段(birthDay)進行拓展,dateFormat 的形式
     */
    private String birthDateFormat;
    /**
     * 對 DO 里面的字段(birthDay)進行拓展,expression 的形式
     */
    private String birthExpressionFormat;

}

四、多對一用法

說明:
寫一個 Mapper 接口 PersonConverter赡矢,其中兩個方法杭朱,一個是單實體映射,另一個是List映射

對象屬性與目標(biāo)對象屬性名字一致

  • 自動映射對應(yīng)屬性吹散,啥也不用改

不一致

1.屬性名不一致:

source---target:

2.類型不一致:

3.不映射弧械,可忽略

@Mapper
public interface PersonConverter {
    PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);
    @Mappings({
        @Mapping(source = "birthday", target = "birth"),
        @Mapping(source = "birthday", target = "birthDateFormat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
        @Mapping(target = "birthExpressionFormat", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(person.getBirthday(),\"yyyy-MM-dd HH:mm:ss\"))"),
        @Mapping(source = "user.age", target = "age"),
        @Mapping(target = "email", ignore = true)
    })
    PersonDTO domain2dto(Person person);

    List<PersonDTO> domain2dto(List<Person> people);
}

五、轉(zhuǎn)換類中添加自定義方法

通過 “默認方法”的方式空民,為轉(zhuǎn)換接口添加自定方法

// 形式如下 
default PersonDTO personToPersonDTO(Person person) {
    //hand-written mapping logic
}

// 比如在 PersonConverter 里面加入如下
default Boolean convert2Bool(Integer value) {
    if (value == null || value < 1) {
        return Boolean.FALSE;
    } else {
        return Boolean.TRUE;
    }
}

default Integer convert2Int(Boolean value) {
    if (value == null) {
        return null;
    }
    if (Boolean.TRUE.equals(value)) {
        return 1;
    }
    return 0;
}

默認方法參見:https://www.cnblogs.com/sidesky/p/9287710.html

六 @InheritConfiguration 繼承之前方法的配置

比如在 PersonConverter 里面加入如下刃唐,@InheritConfiguration 用于繼承剛才domain2dto方法的配置

@InheritConfiguration(name = "domain2dto") 
void update(Person person, @MappingTarget PersonDTO personDTO);

七、注入Spring

默認使用方式

PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);

注入Spring方式 @Mapper(componentModel="spring")

@Mapper(componentModel="spring")
public interface PersonConverter {
    @Mappings({
        @Mapping(source = "birthday", target = "birth"),
        @Mapping(source = "birthday", target = "birthDateFormat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
        @Mapping(target = "birthExpressionFormat", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(person.getBirthday(),\"yyyy-MM-dd HH:mm:ss\"))"),
        @Mapping(source = "user.age", target = "age"),
        @Mapping(target = "email", ignore = true)
    })
    PersonDTO domain2dto(Person person);
}

spring 使用

@RunWith(SpringRunner.class)
@SpringBootTest(classes = BaseTestConfiguration.class)
public class PersonConverterTest {
    //這里把轉(zhuǎn)換器裝配進來
    @Autowired
    private PersonConverter personConverter;
    @Test
    public void test() {
        Person person = new Person(1L,"zhige","zhige.me@gmail.com",new Date(),new User(1));
        PersonDTO personDTO = personConverter.domain2dto(person);
    }
}

八界轩、MapStruct常用注解

@Mapper 只有在接口加上這個注解画饥, MapStruct 才會去實現(xiàn)該接口
    @Mapper 里有個 componentModel 屬性,主要是指定實現(xiàn)類的類型浊猾,一般用到兩個
    default:默認抖甘,可以通過 Mappers.getMapper(Class) 方式獲取實例對象
    spring:在接口的實現(xiàn)類上自動添加注解 @Component,可通過 @Autowired 方式注入
@Mapping:屬性映射葫慎,若源對象屬性與目標(biāo)對象名字一致衔彻,會自動映射對應(yīng)屬性
    source:源屬性
    target:目標(biāo)屬性
    dateFormat:String 到 Date 日期之間相互轉(zhuǎn)換,通過 SimpleDateFormat偷办,該值為 SimpleDateFormat              的日期格式
    ignore: 忽略這個字段
@Mappings:配置多個@Mapping
@MappingTarget 用于更新已有對象
@InheritConfiguration 用于繼承配置

參考

MapStruct超級簡單的學(xué)習(xí)筆記

MapStruct學(xué)習(xí)筆記

mapstruct官網(wǎng)

mapstruct文檔

淺析VO艰额、DTO、DO椒涯、PO的概念柄沮、區(qū)別和用處

MapStruct處理Java中枚舉Enum類型使用與舉例

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子祖搓,更是在濱河造成了極大的恐慌狱意,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棕硫,死亡現(xiàn)場離奇詭異髓涯,居然都是意外死亡,警方通過查閱死者的電腦和手機哈扮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門纬纪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人滑肉,你說我怎么就攤上這事包各。” “怎么了靶庙?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵问畅,是天一觀的道長。 經(jīng)常有香客問我六荒,道長护姆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任掏击,我火速辦了婚禮卵皂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘砚亭。我一直安慰自己灯变,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布捅膘。 她就那樣靜靜地躺著添祸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪寻仗。 梳的紋絲不亂的頭發(fā)上刃泌,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天,我揣著相機與錄音署尤,去河邊找鬼蔬咬。 笑死,一個胖子當(dāng)著我的面吹牛沐寺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盖奈,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼混坞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起究孕,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤啥酱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后厨诸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镶殷,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年微酬,在試婚紗的時候發(fā)現(xiàn)自己被綠了绘趋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡颗管,死狀恐怖陷遮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情垦江,我是刑警寧澤帽馋,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站比吭,受9級特大地震影響绽族,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜衩藤,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一吧慢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧慷彤,春花似錦娄蔼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至跋选,卻和暖如春涕癣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背前标。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工坠韩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炼列。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓只搁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親俭尖。 傳聞我的和親對象是個殘疾皇子氢惋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容