<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    推薦一款 Java 對象映射神器!

    共 5798字,需瀏覽 12分鐘

     ·

    2020-11-27 03:52

    點擊上方“JAVA”,星標(biāo)公眾號

    重磅干貨,第一時間送達(dá)

    前言

    按照日常開發(fā)習(xí)慣,對于不同領(lǐng)域?qū)邮褂貌煌琂avaBean對象傳輸數(shù)據(jù),避免相互影響,因此基于數(shù)據(jù)庫實體對象User衍生出比如UserDto、UserVo等對象,于是在不同層之間進(jìn)行數(shù)據(jù)傳輸時,不可避免地需要將這些對象進(jìn)行互相轉(zhuǎn)換操作。
    常見的轉(zhuǎn)換方式有:
    • 調(diào)用getter/setter方法進(jìn)行屬性賦值
    • 調(diào)用BeanUtil.copyPropertie進(jìn)行反射屬性賦值

    第一種方式不必說,屬性多了就需要寫一大坨getter/setter代碼。第二種方式比第一種方式要簡便很多,但是坑巨多,比如sources與target寫反,難以定位某個字段在哪里進(jìn)行的賦值,同時因為用到反射,導(dǎo)致性能也不佳。

    鑒于此,今天寫一寫第三種對象轉(zhuǎn)換方式,本文使用的是 MapStruct 工具進(jìn)行轉(zhuǎn)換,MapStruct 原理也很簡單,就是在代碼編譯階段生成對應(yīng)的賦值代碼,底層原理還是調(diào)用getter/setter方法,但是這是由工具替我們完成,MapStruct在不影響性能的情況下,解決了前面兩種方式弊端,很贊~

    準(zhǔn)備工作

    為了講解 MapStruct 工具的使用,本文使用常見的 User 類以及對應(yīng) UserDto 對象來演示。
    @Data
    @Accessors(chain = true)
    public?class?User {
    ????private?Long id;
    ????private?String?username;
    ????private?String?password;
    ????private?Integer sex;
    ????private?LocalDate birthday;
    ????private?LocalDateTime createTime;
    ????private?String?config;
    ????private?String?test;
    }

    @Data
    @Accessors(chain = true)
    public?class?UserVo {
    ????private?Long id;
    ????private?String?username;
    ????private?String?password;
    ????private?Integer gender;
    ????private?LocalDate birthday;
    ????private?String?createTime;
    ????private?List config;
    ????private?String?test;
    ????@Data
    ????public?static?class?UserConfig {
    ????????private?String?field1;
    ????????private?Integer field2;
    ????}
    }
    注意觀察這兩個類的區(qū)別。

    一、MapStruct 配置以及基礎(chǔ)使用

    項目中引入 MapStruct 的依賴
    <dependency>
    ??<groupId>org.mapstructgroupId>

    ??<artifactId>mapstructartifactId>
    ??<version>1.3.1.Finalversion>
    dependency>
    <dependency>
    ??<groupId>org.mapstructgroupId>
    ??<artifactId>mapstruct-processorartifactId>
    ??<version>1.3.1.Finalversion>
    dependency>
    因為項目中的對象轉(zhuǎn)換操作基本都一樣,因此抽取除了一個轉(zhuǎn)換基類,不同對象如果只是簡單轉(zhuǎn)換可以直接繼承該基類,而無需覆寫基類任何方法,即只需要一個空類即可。如果子類覆寫了基類的方法,則基類上的 @Mapping 會失效。
    @MapperConfig
    public interface BaseMapping {

    ????/**
    ?????* 映射同名屬性
    ?????*/

    ????@Mapping(target?= "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    ????TARGET sourceToTarget(SOURCE var1);

    ????/**
    ?????* 反向,映射同名屬性
    ?????*/

    ????@InheritInverseConfiguration(name?= "sourceToTarget")
    ????SOURCE targetToSource(TARGET var1);

    ????/**
    ?????* 映射同名屬性,集合形式
    ?????*/

    ????@InheritConfiguration(name?= "sourceToTarget")
    ????List sourceToTarget(List var1);

    ????/**
    ?????* 反向,映射同名屬性,集合形式
    ?????*/

    ????@InheritConfiguration(name?= "targetToSource")
    ????List targetToSource(List var1);

    ????/**
    ?????* 映射同名屬性,集合流形式
    ?????*/

    ????List<TARGET> sourceToTarget(Stream<SOURCE> stream);

    ????/**
    ?????* 反向,映射同名屬性,集合流形式
    ?????*/

    ????List<SOURCE> targetToSource(Stream<TARGET> stream);
    }
    實現(xiàn) User 與 UserVo 對象的轉(zhuǎn)換器
    import?org.mapstruct.Mapper;
    import?org.mapstruct.Mapping;

    @Mapper(componentModel = "spring")
    public?interface?UserMapping?extends?BaseMapping<User, UserVo> {

    ????@Mapping(target = "gender", source = "sex")
    ????@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    ????@Override
    ????UserVo sourceToTarget(User var1);

    ????@Mapping(target = "sex", source = "gender")
    ????@Mapping(target = "password", ignore = true)
    ????@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    ????@Override
    ????User targetToSource(UserVo var1);

    ????default?List strConfigToListUserConfig(String config)?{
    ????????return?JSON.parseArray(config, UserConfig.class);
    ????}

    ????default?String listUserConfigToStrConfig(List list)?{
    ????????return?JSON.toJSONString(list);
    ????}
    }
    本文示例使用的是 Spring 的方式,@Mapper 注解的 componentModel 屬性值為 spring,不過應(yīng)該大多數(shù)都用的此模式進(jìn)行開發(fā)。
    @Mapping用于配置對象的映射關(guān)系,示例中 User 對象性別屬性名為 sex,而UserVo對象性別屬性名為gender,因此需要配置 target 與 source 屬性。
    password 字段不應(yīng)該返回到前臺,可以采取兩種方式不進(jìn)行轉(zhuǎn)換,第一種就是在vo對象中不出現(xiàn)password字段,第二種就是在@Mapping中設(shè)置該字段 ignore = true。
    MapStruct 提供了時間格式化的屬性 dataFormat,支持Date、LocalDate、LocalDateTime等時間類型與String的轉(zhuǎn)換。示例中birthday 屬性為 LocalDate 類型,可以無需指定dataFormat自動完成轉(zhuǎn)換,而LocalDateTime類型默認(rèn)使用的是ISO格式時間,在國內(nèi)往往不符合需求,因此需要手動指定一下 dataFormat。

    二、自定義屬性類型轉(zhuǎn)換方法

    一般常用的類型字段轉(zhuǎn)換 MapStruct都能替我們完成,但是有一些是我們自定義的對象類型,MapStruct就不能進(jìn)行字段轉(zhuǎn)換,這就需要我們編寫對應(yīng)的類型轉(zhuǎn)換方法,筆者使用的是JDK8,支持接口中的默認(rèn)方法,可以直接在轉(zhuǎn)換器中添加自定義類型轉(zhuǎn)換方法。
    示例中User對象的config屬性是一個JSON字符串,UserVo對象中是List類型的,這需要實現(xiàn)JSON字符串與對象的互轉(zhuǎn)。
    default?List strConfigToListUserConfig(String config) {
    ??return?JSON.parseArray(config, UserConfig.class);
    }

    default?String listUserConfigToStrConfig(List list) {
    ??return?JSON.toJSONString(list);
    }
    如果是 JDK8以下的,不支持默認(rèn)方法,可以另外定義一個 轉(zhuǎn)換器,然后再當(dāng)前轉(zhuǎn)換器的 @Mapper 中通過 uses = XXX.class 進(jìn)行引用。
    定義好方法之后,MapStruct當(dāng)匹配到合適類型的字段時,會調(diào)用我們自定義的轉(zhuǎn)換方法進(jìn)行轉(zhuǎn)換。

    三、單元測試


    @Slf4j
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public?class?MapStructTest?{

    ??@Resource
    ??private?UserMapping userMapping;

    ??@Test
    ??public?void?tetDomain2DTO()?{
    ????User user = new?User()
    ??????.setId(1L)
    ??????.setUsername("zhangsan")
    ??????.setSex(1)
    ??????.setPassword("abc123")
    ??????.setCreateTime(LocalDateTime.now())
    ??????.setBirthday(LocalDate.of(1999, 9, 27))
    ??????.setConfig("[{\"field1\":\"Test Field1\",\"field2\":500}]");
    ????UserVo userVo = userMapping.sourceToTarget(user);
    ????log.info("User: {}", user);
    ????
    ????log.info("UserVo: {}", userVo);
    ????
    ??}

    ??@Test
    ??public?void?testDTO2Domain()?{
    ????UserConfig userConfig = new?UserConfig();
    ????userConfig.setField1("Test Field1");
    ????userConfig.setField2(500);

    ????UserVo userVo = new?UserVo()
    ??????.setId(1L)
    ??????.setUsername("zhangsan")
    ??????.setGender(2)
    ??????.setCreateTime("2020-01-18 15:32:54")
    ??????.setBirthday(LocalDate.of(1999, 9, 27))
    ??????.setConfig(Collections.singletonList(userConfig));
    ????User user = userMapping.targetToSource(userVo);
    ????log.info("UserVo: {}", userVo);
    ????
    ????log.info("User: {}", user);
    ????
    ??}


    四、常見問題

    當(dāng)兩個對象屬性不一致時,比如User對象中某個字段不存在與UserVo當(dāng)中時,在編譯時會有警告提示,可以在@Mapping中配置 ignore = true,當(dāng)字段較多時,可以直接在@Mapper中設(shè)置unmappedTargetPolicy屬性或者unmappedSourcePolicy屬性為 ReportingPolicy.IGNORE即可。
    如果項目中也同時使用到了 Lombok,一定要注意 Lombok的版本要等于或者高于1.18.10,否則會有編譯不通過的情況發(fā)生,筆者掉進(jìn)這個坑很久才爬了出來,希望各位不要重復(fù)踩坑。

    代碼下載

    本文涉及代碼已上傳到 Github,以供參考。
    https://github.com/Mosiki/learning-modules/tree/master/learning-mapstruct

    參考

    https://mapstruct.org/documentation/stable/reference/html/

    https://mapstruct.org/faq/

    https://github.com/mapstruct/mapstruct-examples

    http://www.kailing.pub/MapStruct1.3/index.html

    https://www.cnblogs.com/javaguide/p/11861749.html

    最近熬夜給大家準(zhǔn)備了515套Java代碼,有一些是業(yè)務(wù)類的小項目,比如Java博客項目,也有腳手架、也有平時用一些的工具類、21套小程序代碼,也有一些游戲類的項目。

    掃以下二維碼并回復(fù)“828”即可獲取


    或者在本公眾號對話框回復(fù)【828】馬上獲取

    瀏覽 57
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    五月天中文字幕 | 草草影院网站 | 狅操美女嫩穴传媒 | 叼嗨片| 无码人妻一区二区三区密桃手机版 |