成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

推薦一個(gè) Java 實(shí)體映射工具 MapStruct

wayneli / 3085人閱讀

摘要:聲明業(yè)務(wù)實(shí)體對(duì)象,數(shù)據(jù)傳輸對(duì)象。這種對(duì)象與對(duì)象之間的互相轉(zhuǎn)換,就需要有一個(gè)專門用來(lái)解決轉(zhuǎn)換問(wèn)題的工具,畢竟每一個(gè)字段都會(huì)很麻煩。就是這樣的一個(gè)屬性映射工具,只需要定義一個(gè)接口,就會(huì)自動(dòng)實(shí)現(xiàn)這個(gè)映射接口,避免了復(fù)雜繁瑣的映射實(shí)現(xiàn)。

聲明:
1、DO(業(yè)務(wù)實(shí)體對(duì)象),DTO(數(shù)據(jù)傳輸對(duì)象)。
2、我的代碼中用到了 Lombok ,不了解的可以自行了解一下,了解的忽略這條就好。

在一個(gè)成熟的工程中,尤其是現(xiàn)在的分布式系統(tǒng)中,應(yīng)用與應(yīng)用之間,還有多帶帶的應(yīng)用細(xì)分模塊之后,DO 一般不會(huì)讓外部依賴,這時(shí)候需要在提供對(duì)外接口的模塊里放 DTO 用于對(duì)象傳輸,也即是 DO 對(duì)象對(duì)內(nèi),DTO對(duì)象對(duì)外,DTO 可以根據(jù)業(yè)務(wù)需要變更,并不需要映射 DO 的全部屬性。

這種 對(duì)象與對(duì)象之間的互相轉(zhuǎn)換,就需要有一個(gè)專門用來(lái)解決轉(zhuǎn)換問(wèn)題的工具,畢竟每一個(gè)字段都 get/set 會(huì)很麻煩。

MapStruct 就是這樣的一個(gè)屬性映射工具,只需要定義一個(gè) Mapper 接口,MapStruct 就會(huì)自動(dòng)實(shí)現(xiàn)這個(gè)映射接口,避免了復(fù)雜繁瑣的映射實(shí)現(xiàn)。MapStruct官網(wǎng)地址:?http://mapstruct.org/

工程中引入 maven 依賴

     1.2.0.Final



    
      org.mapstruct
      mapstruct-jdk8
      ${mapstruct.version}
    
    
      org.mapstruct
      mapstruct-processor
      ${mapstruct.version}
    
基本映射

這里定義兩個(gè) DO 對(duì)象 Person 和 User,其中 user 是 Person 的一個(gè)屬性 ,一個(gè) DTO 對(duì)象 PersonDTO

@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;
    /**
     * 對(duì)應(yīng) Person.user.age
     */
    private Integer age;
    private String email;
    /**
     * 與 DO 里面的字段名稱(birthDay)不一致
     */
    private Date birth;
    /**
     * 對(duì) DO 里面的字段(birthDay)進(jìn)行拓展,dateFormat 的形式
     */
    private String birthDateFormat;
    /**
     * 對(duì) DO 里面的字段(birthDay)進(jìn)行拓展,expression 的形式
     */
    private String birthExpressionFormat;

}

寫(xiě)一個(gè) Mapper 接口 PersonConverter,其中兩個(gè)方法,一個(gè)是單實(shí)體映射,另一個(gè)是List映射

若源對(duì)象屬性與目標(biāo)對(duì)象屬性名字一致,會(huì)自動(dòng)映射對(duì)應(yīng)屬性,不一樣的需要指定,也可以用 format 轉(zhuǎn)成自己想要的類型,也支持表達(dá)式的方式,可以看到像 id、name、email這些名詞一致的我并沒(méi)有指定 source-target,而birthday-birth指定了,轉(zhuǎn)換格式的 birthDateFormat 加了dateFormat 或者 birthExpressionFormat 加了 expression,如果某個(gè)屬性你不想映射,可以加個(gè) ignore=true

@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 domain2dto(List people);
}

編譯MapStruct之后,手工編譯或者啟動(dòng) IDE 的時(shí)候 IDE 也會(huì)幫我們編譯, 會(huì)自動(dòng)在 target/classes 下生成對(duì)應(yīng)的實(shí)現(xiàn)類

手工編譯命令
mvn compile

注意?。?!下面這個(gè) PersonConverterImpl 是自動(dòng)生成的,不是自己寫(xiě)的!

public class PersonConverterImpl implements PersonConverter {
    public PersonConverterImpl() {
    }

    public PersonDTO domain2dto(Person person) {
        if (person == null) {
            return null;
        } else {
            PersonDTO personDTO = new PersonDTO();
            personDTO.setBirth(person.getBirthday());
            if (person.getBirthday() != null) {
                personDTO.setBirthDateFormat((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(person.getBirthday()));
            }

            Integer age = this.personUserAge(person);
            if (age != null) {
                personDTO.setAge(age);
            }

            personDTO.setId(person.getId());
            personDTO.setName(person.getName());
            personDTO.setBirthExpressionFormat(DateFormatUtils.format(person.getBirthday(), "yyyy-MM-dd HH:mm:ss"));
            return personDTO;
        }
    }

    public List domain2dto(List people) {
        if (people == null) {
            return null;
        } else {
            List list = new ArrayList(people.size());
            Iterator var3 = people.iterator();

            while(var3.hasNext()) {
                Person person = (Person)var3.next();
                list.add(this.domain2dto(person));
            }

            return list;
        }
    }

    private Integer personUserAge(Person person) {
        if (person == null) {
            return null;
        } else {
            User user = person.getUser();
            if (user == null) {
                return null;
            } else {
                Integer age = user.getAge();
                return age == null ? null : age;
            }
        }
    }
}

寫(xiě)一個(gè)單元測(cè)試類 PersonConverterTest 測(cè)試一下,看看效果

public class PersonConverterTest {
    @Test
    public void test() {
        Person person = new Person(1L,"zhige","[email protected]",new Date(),new User(1));
        PersonDTO personDTO = PersonConverter.INSTANCE.domain2dto(person);
        assertNotNull(personDTO);
        assertEquals(personDTO.getId(), person.getId());
        assertEquals(personDTO.getName(), person.getName());
        assertEquals(personDTO.getBirth(), person.getBirthday());
        String format = DateFormatUtils.format(personDTO.getBirth(), "yyyy-MM-dd HH:mm:ss");
        assertEquals(personDTO.getBirthDateFormat(),format);
        assertEquals(personDTO.getBirthExpressionFormat(),format);

        List people = new ArrayList<>();
        people.add(person);
        List personDTOs = PersonConverter.INSTANCE.domain2dto(people);
        assertNotNull(personDTOs);
    }
}
多對(duì)一

MapStruct 可以將幾種類型的對(duì)象映射為另外一種類型,比如將多個(gè) DO 對(duì)象轉(zhuǎn)換為 DTO

例子

兩個(gè) DO 對(duì)象 Item 和 Sku,一個(gè) DTO 對(duì)象 SkuDTO

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Item {
    private Long id;
    private String title;
}

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Sku {
    private Long id;
    private String code;
    private Integer price;
}

@NoArgsConstructor
@AllArgsConstructor
@Data
public class SkuDTO {
    private Long skuId;
    private String skuCode;
    private Integer skuPrice;
    private Long itemId;
    private String itemName;
}

創(chuàng)建 ItemConverter(映射)接口,MapStruct 就會(huì)自動(dòng)實(shí)現(xiàn)該接口

@Mapper
public interface ItemConverter {
    ItemConverter INSTANCE = Mappers.getMapper(ItemConverter.class);

    @Mappings({
            @Mapping(source = "sku.id",target = "skuId"),
            @Mapping(source = "sku.code",target = "skuCode"),
            @Mapping(source = "sku.price",target = "skuPrice"),
            @Mapping(source = "item.id",target = "itemId"),
            @Mapping(source = "item.title",target = "itemName")
    })
    SkuDTO domain2dto(Item item, Sku sku);
}

創(chuàng)建測(cè)試類,講 Item 和 Sku 兩個(gè) DO對(duì)象,映射成一個(gè) DTO 對(duì)象 SkuDTO

public class ItemConverterTest {
    @Test
    public void test() {
        Item item = new Item(1L, "iPhone X");
        Sku sku = new Sku(2L, "phone12345", 1000000);
        SkuDTO skuDTO = ItemConverter.INSTANCE.domain2dto(item, sku);
        assertNotNull(skuDTO);
        assertEquals(skuDTO.getSkuId(),sku.getId());
        assertEquals(skuDTO.getSkuCode(),sku.getCode());
        assertEquals(skuDTO.getSkuPrice(),sku.getPrice());
        assertEquals(skuDTO.getItemId(),item.getId());
        assertEquals(skuDTO.getItemName(),item.getTitle());
    }
}
可以添加自定義方法
// 形式如下 
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;
}
// 測(cè)試類 PersonConverterTest 加入
assertTrue(PersonConverter.INSTANCE.convert2Bool(1));
assertEquals((int)PersonConverter.INSTANCE.convert2Int(true),1);

#### 如果已經(jīng)有了接收對(duì)象,更新目標(biāo)對(duì)象

// 比如在 PersonConverter 里面加入如下,@InheritConfiguration 用于繼承剛才的配置
@InheritConfiguration(name = "domain2dto")
void update(Person person, @MappingTarget PersonDTO personDTO);

// 測(cè)試類 PersonConverterTest 加入如下
Person person = new Person(1L,"zhige","[email protected]",new Date(),new User(1));
PersonDTO personDTO = PersonConverter.INSTANCE.domain2dto(person);
assertEquals("zhige", personDTO.getName());
person.setName("xiaozhi");
PersonConverter.INSTANCE.update(person, personDTO);
assertEquals("xiaozhi", personDTO.getName());
Spring 注入的方式
// 剛才一直寫(xiě)的例子是默認(rèn)的方式
PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);

還有一種常用的方式,是和常用的框架 Spring 結(jié)合,在 @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);
}

這時(shí)候測(cè)試類改一下,我用的 spring boot 的形式

@RunWith(SpringRunner.class)
@SpringBootTest(classes = BaseTestConfiguration.class)
public class PersonConverterTest {
    //這里把轉(zhuǎn)換器裝配進(jìn)來(lái)
    @Autowired
    private PersonConverter personConverter;
    @Test
    public void test() {
        Person person = new Person(1L,"zhige","[email protected]",new Date(),new User(1));
        PersonDTO personDTO = personConverter.domain2dto(person);

        assertNotNull(personDTO);
        assertEquals(personDTO.getId(), person.getId());
        assertEquals(personDTO.getName(), person.getName());
        assertEquals(personDTO.getBirth(), person.getBirthday());
        String format = DateFormatUtils.format(personDTO.getBirth(), "yyyy-MM-dd HH:mm:ss");
        assertEquals(personDTO.getBirthDateFormat(),format);
        assertEquals(personDTO.getBirthExpressionFormat(),format);

    }
}

我 test 路徑下加入了一個(gè)配置類

@EnableAutoConfiguration
@Configuration
@ComponentScan
public class BaseTestConfiguration {
}
MapStruct 注解的關(guān)鍵詞
@Mapper 只有在接口加上這個(gè)注解, MapStruct 才會(huì)去實(shí)現(xiàn)該接口
    @Mapper 里有個(gè) componentModel 屬性,主要是指定實(shí)現(xiàn)類的類型,一般用到兩個(gè)
    default:默認(rèn),可以通過(guò) Mappers.getMapper(Class) 方式獲取實(shí)例對(duì)象
    spring:在接口的實(shí)現(xiàn)類上自動(dòng)添加注解 @Component,可通過(guò) @Autowired 方式注入
@Mapping:屬性映射,若源對(duì)象屬性與目標(biāo)對(duì)象名字一致,會(huì)自動(dòng)映射對(duì)應(yīng)屬性
    source:源屬性
    target:目標(biāo)屬性
    dateFormat:String 到 Date 日期之間相互轉(zhuǎn)換,通過(guò) SimpleDateFormat,該值為 SimpleDateFormat                 的日期格式
    ignore: 忽略這個(gè)字段
@Mappings:配置多個(gè)@Mapping
@MappingTarget 用于更新已有對(duì)象
@InheritConfiguration 用于繼承配置

本文只是寫(xiě)了一些常用的比較簡(jiǎn)單的一些功能,更詳細(xì)的可以去閱讀官方文檔:? http://mapstruct.org/document...

如果覺(jué)得內(nèi)容還不錯(cuò),可以關(guān)注一下我哦
微信公眾號(hào):志哥 (ID: zhige-me)
期待與你相遇,一同成長(zhǎng)前行!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/71214.html

相關(guān)文章

  • 第三十章:SpringBoot使用MapStruct自動(dòng)映射DTO

    摘要:商品類型實(shí)體恒宇少年碼云商品基本信息實(shí)體恒宇少年碼云接下來(lái)我們繼續(xù)創(chuàng)建相關(guān)的。注解是用于標(biāo)注接口抽象類是被自動(dòng)映射的標(biāo)識(shí),只有存在該注解才會(huì)將內(nèi)部的接口方法自動(dòng)實(shí)現(xiàn)。 MapStruct是一種類型安全的bean映射類生成java注釋處理器。我們要做的就是定義一個(gè)映射器接口,聲明任何必需的映射方法。在編譯的過(guò)程中,MapStruct會(huì)生成此接口的實(shí)現(xiàn)。該實(shí)現(xiàn)使用純java方法調(diào)用的源和目...

    weakish 評(píng)論0 收藏0
  • Java Bean Copy 性能大比拼

    摘要:性能大比拼簡(jiǎn)介拷貝在工作中被大量使用,可以大幅度的提高工作量。本文對(duì)常用的工具進(jìn)行了壓力測(cè)試,方便大家選擇更加適合自己的工具。本篇文章是增強(qiáng)介紹續(xù)篇,該專欄會(huì)持續(xù)更新,感興趣的朋友請(qǐng)訂閱我們。的表現(xiàn)反而比更好,可能是模型不一樣導(dǎo)致的。 Java Bean Copy 性能大比拼 簡(jiǎn)介 Bean 拷貝在工作中被大量使用,可以大幅度的提高工作量。本文對(duì)常用的 Bean copy 工具進(jìn)行了...

    lentoo 評(píng)論0 收藏0
  • [開(kāi)源作品] skadmin 管理系統(tǒng)

    摘要:簡(jiǎn)介項(xiàng)目基于的前后端分離的管理系統(tǒng),項(xiàng)目采用分模塊開(kāi)發(fā)方式,權(quán)限控制采用,基于角色的訪問(wèn)控制,支持?jǐn)?shù)據(jù)字典數(shù)據(jù)權(quán)限管理前端菜單支持動(dòng)態(tài)路由,另外還有其他的功能模塊日志管理代碼生成器系統(tǒng)監(jiān)控云存儲(chǔ)管理系統(tǒng)工具等等。 簡(jiǎn)介 項(xiàng)目基于 Spring Boot 2.1.0 、 Spring Data JPA、 Spring Security、Redis、Vue的前后端分離的管理系統(tǒng),項(xiàng)目采用分...

    codergarden 評(píng)論0 收藏0
  • 從零開(kāi)始搭建SSM框架(Spring + Spring MVC + Mybatis)

    摘要:打開(kāi),,選中,然后再選中,輸入項(xiàng)目的和,指定等配置,修改,打開(kāi)項(xiàng)目,添加一些必要的目錄,最終項(xiàng)目框架目錄圖如下修改文件,指定各依賴和插件的版本等信息在標(biāo)簽里面管理各依賴的版本號(hào)添加項(xiàng)目依賴管理依賴配置好之后,開(kāi)始整合。 最近在回顧和總結(jié)一些技術(shù),想到了把之前比較火的 SSM 框架重新搭建出來(lái),作為一個(gè)小結(jié),同時(shí)也希望本文章寫(xiě)出來(lái)能對(duì)大家有一些幫助和啟發(fā),因本人水平有限,難免可能會(huì)有一些...

    MiracleWong 評(píng)論0 收藏0
  • 構(gòu)建高性能Java持久層的14個(gè)建議

    摘要:系列文章地址原文地址一個(gè)高性能的數(shù)據(jù)訪問(wèn)層需要很多關(guān)于數(shù)據(jù)庫(kù)的內(nèi)部結(jié)構(gòu)以及很多優(yōu)化商業(yè)應(yīng)用的技術(shù)建議。在語(yǔ)句中的表現(xiàn)最好,不過(guò)不能使用約束,數(shù)據(jù)完整性的控制較差。應(yīng)用層的緩存則利用高速副本的方式來(lái)保證低響應(yīng)時(shí)間。 Github系列文章地址 原文地址 Introduction 一個(gè)高性能的數(shù)據(jù)訪問(wèn)層需要很多關(guān)于數(shù)據(jù)庫(kù)的內(nèi)部結(jié)構(gòu)、JDBC、JPA、Hibernate以及很多優(yōu)化商業(yè)應(yīng)用...

    shenhualong 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<