<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          推薦一款 Java 對(duì)象映射神器!

          共 5798字,需瀏覽 12分鐘

           ·

          2020-11-27 03:52

          點(diǎn)擊上方“JAVA”,星標(biāo)公眾號(hào)

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

          前言

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

          第一種方式不必說(shuō),屬性多了就需要寫一大坨getter/setter代碼。第二種方式比第一種方式要簡(jiǎn)便很多,但是坑巨多,比如sources與target寫反,難以定位某個(gè)字段在哪里進(jìn)行的賦值,同時(shí)因?yàn)橛玫椒瓷洌瑢?dǎo)致性能也不佳。

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

          準(zhǔn)備工作

          為了講解 MapStruct 工具的使用,本文使用常見(jiàn)的 User 類以及對(duì)應(yīng) UserDto 對(duì)象來(lái)演示。
          @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;
          ????}
          }
          注意觀察這兩個(gè)類的區(qū)別。

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

          項(xiàng)目中引入 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>
          因?yàn)轫?xiàng)目中的對(duì)象轉(zhuǎn)換操作基本都一樣,因此抽取除了一個(gè)轉(zhuǎn)換基類,不同對(duì)象如果只是簡(jiǎn)單轉(zhuǎn)換可以直接繼承該基類,而無(wú)需覆寫基類任何方法,即只需要一個(gè)空類即可。如果子類覆寫了基類的方法,則基類上的 @Mapping 會(huì)失效。
          @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);
          }
          實(shí)現(xiàn) User 與 UserVo 對(duì)象的轉(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,不過(guò)應(yīng)該大多數(shù)都用的此模式進(jìn)行開發(fā)。
          @Mapping用于配置對(duì)象的映射關(guān)系,示例中 User 對(duì)象性別屬性名為 sex,而UserVo對(duì)象性別屬性名為gender,因此需要配置 target 與 source 屬性。
          password 字段不應(yīng)該返回到前臺(tái),可以采取兩種方式不進(jìn)行轉(zhuǎn)換,第一種就是在vo對(duì)象中不出現(xiàn)password字段,第二種就是在@Mapping中設(shè)置該字段 ignore = true。
          MapStruct 提供了時(shí)間格式化的屬性 dataFormat,支持Date、LocalDate、LocalDateTime等時(shí)間類型與String的轉(zhuǎn)換。示例中birthday 屬性為 LocalDate 類型,可以無(wú)需指定dataFormat自動(dòng)完成轉(zhuǎn)換,而LocalDateTime類型默認(rèn)使用的是ISO格式時(shí)間,在國(guó)內(nèi)往往不符合需求,因此需要手動(dòng)指定一下 dataFormat。

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

          一般常用的類型字段轉(zhuǎn)換 MapStruct都能替我們完成,但是有一些是我們自定義的對(duì)象類型,MapStruct就不能進(jìn)行字段轉(zhuǎn)換,這就需要我們編寫對(duì)應(yīng)的類型轉(zhuǎn)換方法,筆者使用的是JDK8,支持接口中的默認(rèn)方法,可以直接在轉(zhuǎn)換器中添加自定義類型轉(zhuǎn)換方法。
          示例中User對(duì)象的config屬性是一個(gè)JSON字符串,UserVo對(duì)象中是List類型的,這需要實(shí)現(xiàn)JSON字符串與對(duì)象的互轉(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)方法,可以另外定義一個(gè) 轉(zhuǎn)換器,然后再當(dāng)前轉(zhuǎn)換器的 @Mapper 中通過(guò) uses = XXX.class 進(jìn)行引用。
          定義好方法之后,MapStruct當(dāng)匹配到合適類型的字段時(shí),會(huì)調(diào)用我們自定義的轉(zhuǎn)換方法進(jìn)行轉(zhuǎn)換。

          三、單元測(cè)試


          @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);
          ????
          ??}


          四、常見(jiàn)問(wèn)題

          當(dāng)兩個(gè)對(duì)象屬性不一致時(shí),比如User對(duì)象中某個(gè)字段不存在與UserVo當(dāng)中時(shí),在編譯時(shí)會(huì)有警告提示,可以在@Mapping中配置 ignore = true,當(dāng)字段較多時(shí),可以直接在@Mapper中設(shè)置unmappedTargetPolicy屬性或者unmappedSourcePolicy屬性為 ReportingPolicy.IGNORE即可。
          如果項(xiàng)目中也同時(shí)使用到了 Lombok,一定要注意 Lombok的版本要等于或者高于1.18.10,否則會(huì)有編譯不通過(guò)的情況發(fā)生,筆者掉進(jìn)這個(gè)坑很久才爬了出來(lái),希望各位不要重復(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ù)類的小項(xiàng)目,比如Java博客項(xiàng)目,也有腳手架、也有平時(shí)用一些的工具類、21套小程序代碼,也有一些游戲類的項(xiàng)目。

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


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

          瀏覽 57
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  婷婷精品视频 | 91人妻中文字幕在线精品 | 中文在线а√在线 | 欧美日本黄色一级视频 | 久久久久久久久成人电影 |