欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot中MapStruct實(shí)現(xiàn)優(yōu)雅的數(shù)據(jù)復(fù)制

 更新時(shí)間:2024年08月30日 11:03:50   作者:MoCrane  
本文主要介紹了SpringBoot中MapStruct實(shí)現(xiàn)優(yōu)雅的數(shù)據(jù)復(fù)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

你是否在做項(xiàng)目時(shí)遇到過(guò)以下情況:

  • DTO(數(shù)據(jù)傳輸對(duì)象)與Entity之間的轉(zhuǎn)換:在Java的Web應(yīng)用中,通常不會(huì)直接將數(shù)據(jù)庫(kù)中的Entity實(shí)體對(duì)象返回給前端。而是會(huì)創(chuàng)建一個(gè)DTO對(duì)象,這個(gè)DTO對(duì)象只包含需要返回給前端的字段。此時(shí),就需要將Entity轉(zhuǎn)換為DTO。
  • 復(fù)雜對(duì)象的映射:當(dāng)需要映射的對(duì)象包含大量的字段,或者字段之間存在復(fù)雜的依賴(lài)關(guān)系時(shí),手動(dòng)編寫(xiě)映射代碼不僅繁瑣,而且容易出錯(cuò)。

1.為什么選擇MapStruct

1.1.常見(jiàn)的屬性映射方法

一般來(lái)說(shuō),不使用MapStruct框架進(jìn)行屬性映射,常有的方法以下兩種:

  • Getter/Setter方法手動(dòng)映射

這種方法最樸素,手動(dòng)編寫(xiě)代碼將源對(duì)象的屬性存入目標(biāo)對(duì)象,需要注意實(shí)體類(lèi)中嵌套屬性的判空操作以防止空指針異常。

  • BeanUtils.copyProperties()方法進(jìn)行映射

BeanUtils底層使用的是反射機(jī)制實(shí)現(xiàn)屬性的映射。反射是一種在運(yùn)行時(shí)動(dòng)態(tài)獲取類(lèi)信息、調(diào)用方法或訪問(wèn)字段的機(jī)制,無(wú)法利用JVM的優(yōu)化機(jī)制,因此通常比直接方法調(diào)用慢得多。

此外,BeanUtils 只能同屬性映射,或者在屬性相同的情況下,允許被映射的對(duì)象屬性少;但當(dāng)遇到被映射的屬性數(shù)據(jù)類(lèi)型被修改或者被映射的字段名被修改,則會(huì)導(dǎo)致映射失敗。

1.2.MapStruct的優(yōu)勢(shì)

MapStruct是一個(gè)基于注解的Java代碼生成器,它通過(guò)分析帶有@Mapper注解的接口,在編譯時(shí)自動(dòng)生成實(shí)現(xiàn)該接口的映射器類(lèi)。這個(gè)映射器類(lèi)包含了用于執(zhí)行對(duì)象之間映射的具體代碼。

與常規(guī)方法相比,MapStruct具備的優(yōu)勢(shì)有:

  • 簡(jiǎn)化代碼。對(duì)于對(duì)象內(nèi)屬性較多的情況,使用MapStruct框架無(wú)須手動(dòng)對(duì)每個(gè)屬性進(jìn)行g(shù)et/set和屬性判空操作。MapStruct可以通過(guò)注解和映射接口來(lái)定義映射規(guī)則,自動(dòng)生成映射代碼,從而大大簡(jiǎn)化了這種復(fù)雜對(duì)象的映射過(guò)程。

  • 性能優(yōu)越。相較于反射這種映射方法,MapStruct在編譯期生成映射的靜態(tài)代碼,可以充分利用JVM的優(yōu)化機(jī)制,對(duì)于企業(yè)級(jí)的項(xiàng)目應(yīng)用來(lái)說(shuō),這種方式能大大提高數(shù)據(jù)復(fù)制的性能。

  • 類(lèi)型安全。由于MapStruct在編譯期生成映射代碼,這意味著如果源對(duì)象和目標(biāo)對(duì)象的映射存在錯(cuò)誤,那么可以在編譯時(shí)就發(fā)現(xiàn)錯(cuò)誤。相比之下,BeanUtils在運(yùn)行時(shí)使用反射來(lái)執(zhí)行屬性復(fù)制,這可能會(huì)導(dǎo)致類(lèi)型不匹配的問(wèn)題在運(yùn)行時(shí)才發(fā)現(xiàn)。

  • 靈活映射。MapStruct可以輕松處理嵌套對(duì)象和集合的映射。對(duì)于嵌套對(duì)象,MapStruct可以遞歸地應(yīng)用映射規(guī)則;對(duì)于集合,MapStruct可以自動(dòng)迭代集合中的每個(gè)元素并應(yīng)用相應(yīng)的映射規(guī)則。

有開(kāi)發(fā)者對(duì)比過(guò)兩者的性能差距,如下表。這充分體現(xiàn)了MapStruct性能的強(qiáng)大。

對(duì)象轉(zhuǎn)換次數(shù)屬性個(gè)數(shù)BeanUtils耗時(shí)MapStruct耗時(shí)
5千萬(wàn)次614秒1秒
5千萬(wàn)次1536秒1秒
5千萬(wàn)次2555秒1秒

2.MapStruct快速入門(mén)

在快速入門(mén)中,我們的任務(wù)是將dto的數(shù)據(jù)復(fù)制到實(shí)體類(lèi)中。

2.1.導(dǎo)入Maven依賴(lài)

<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.4.2.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.4.2.Final</version>
</dependency>

2.2.創(chuàng)建相關(guān)對(duì)象

注意,實(shí)體類(lèi)要具有g(shù)et/set方法,這里我使用了lombok的@Data注解來(lái)實(shí)現(xiàn)。

import lombok.Data;

/**
 * @author modox
 * @date 2024/5/5
 */
@Data
public class Hotel {
    // 酒店名稱(chēng)
    private String hotelName;

    // 酒店地址
    private String hotelAddress;

    // 所在城市
    private String hotelCity;

    // 聯(lián)系電話
    private String hotelPhone;
}

dto類(lèi)我使用了@Builder注解,可以快速為對(duì)象賦初始值。

import lombok.Builder;
import lombok.Data;

/**
 * @author modox
 * @date 2024/5/5
 */
@Data
@Builder
public class HotelDTO {
    // 酒店名稱(chēng)
    private String name;

    // 酒店地址
    private String address;

    // 所在城市
    private String city;
}

2.3.創(chuàng)建轉(zhuǎn)換器Converter

使用抽象類(lèi)來(lái)定義轉(zhuǎn)換器,只需中@Mapping注解中填寫(xiě)targetsource的字段名,即可實(shí)現(xiàn)屬性復(fù)制。

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;

/**
 * @author modox
 * @date 2024/5/5
 */
@Mapper(componentModel = "spring")
public abstract class TestConverter {
    //酒店詳情
    @Mappings({
            @Mapping(target = "hotelName", source = "name"),
            @Mapping(target = "hotelAddress", source = "address"),
            @Mapping(target = "hotelCity", source = "city"),
    })
    public abstract Hotel dto2Hotel(HotelDTO hotelDTO);
}

2.4.測(cè)試

在SpringBoot的測(cè)試類(lèi)中測(cè)試,這里我使用DTO類(lèi)的@Builder注解提供的方法為dto賦初值模擬實(shí)際開(kāi)發(fā),通過(guò)調(diào)用converter的方法實(shí)現(xiàn)屬性映射。

@Test
  public void Test() {
      HotelDTO build = HotelDTO.builder()
              .name("五星級(jí)酒店")
              .address("中國(guó)")
              .city("北京").build();

      TestConverter converter = new TestConverterImpl();

      Hotel hotel = converter.dto2Hotel(build);

      System.out.println(hotel);
  }

結(jié)果如圖:

最后,我們可以發(fā)現(xiàn)在target包的converter的相同目錄下,生成了TestConverter的實(shí)現(xiàn)類(lèi)

里面為我們編寫(xiě)好了映射的代碼。

3.MapStruct進(jìn)階操作

如果僅是這種簡(jiǎn)單層級(jí)的對(duì)象映射,還不足以體現(xiàn)MapStruct的靈活性。下面將介紹MapStruct的進(jìn)階技巧。

3.1.嵌套映射

假設(shè)我們的Hotel實(shí)體類(lèi)中嵌套了另外一個(gè)實(shí)體類(lèi)Master

@Data
public class Hotel {
    // 酒店名稱(chēng)
    private String hotelName;

    // 酒店地址
    private String hotelAddress;

    // 所在城市
    private String hotelCity;

    // 聯(lián)系電話
    private String hotelPhone;

    private Master master;

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Master {
        private String personName;

        private Integer personAge;
    }
}

dto對(duì)象為:

@Data
@Builder
public class HotelDTO {
    // 酒店名稱(chēng)
    private String name;

    // 酒店地址
    private String address;

    // 所在城市
    private String city;

    private String personName;

    private Integer personAge;
}

我們需要把personNamepersonAge映射到Hotel實(shí)體類(lèi)的Master中,怎么做?

很簡(jiǎn)單,只需要在target屬性中加上Hotel實(shí)體類(lèi)嵌套實(shí)體類(lèi)的字段名,加字符.,再跟上嵌套類(lèi)的字段名即可

//酒店詳情
@Mappings({
        @Mapping(target = "hotelName", source = "name"),
        @Mapping(target = "hotelAddress", source = "address"),
        @Mapping(target = "hotelCity", source = "city"),
        @Mapping(target = "master.personName", source = "personName"),
        @Mapping(target = "master.personAge", source = "personAge"),
})
public abstract Hotel dto2Hotel(HotelDTO hotelDTO);

結(jié)果如圖:

3.2.集合映射

如果源對(duì)象和目標(biāo)對(duì)象的集合的元素類(lèi)型都是基本數(shù)據(jù)類(lèi)型,直接在target和source中填寫(xiě)字段名即可。

若源對(duì)象和目標(biāo)對(duì)象的集合元素類(lèi)型不同,怎么做?

這個(gè)案例我們需要把DTO的personList映射到masterList中。

@Data
@Builder
public class HotelDTO {
    // 酒店名稱(chēng)
    private String name;

    // 酒店地址
    private String address;

    private List<HotelDTO.Person> personList;

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Person {
        private String personName;

        private Integer personAge;
    }
}
@Data
public class Hotel {
    // 酒店名稱(chēng)
    private String hotelName;

    // 酒店地址
    private String hotelAddress;

    private List<Master> masters;

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Master {
        private String name;

        private Integer age;
    }
}

編寫(xiě)converter,這次需要進(jìn)行兩層映射。

第一層將person集合映射到master集合上。

第二層將person對(duì)象的屬性映射到master對(duì)象中。

// 酒店詳情
@Mappings({
        @Mapping(target = "hotelName", source = "name"),
        @Mapping(target = "hotelAddress", source = "address"),
        @Mapping(target = "masters", source = "personList")
})
public abstract Hotel dto2Hotel(HotelDTO hotelDTO);

@Mappings({
        @Mapping(target = "name", source = "personName"),
        @Mapping(target = "age", source = "personAge"),
})
public abstract Hotel.Master toList(HotelDTO.Person person);

結(jié)果如圖:

查看target包下的代碼,可以發(fā)現(xiàn)MapStruct除了兩層映射外,還幫你自動(dòng)生成了迭代集合添加元素的代碼,從而實(shí)現(xiàn)集合元素的復(fù)制。

@Component
public class TestConverterImpl extends TestConverter {
    public TestConverterImpl() {
    }
		// 第一層映射
    public Hotel dto2Hotel(HotelDTO hotelDTO) {
        if (hotelDTO == null) {
            return null;
        } else {
            Hotel hotel = new Hotel();
            hotel.setMasters(this.personListToMasterList(hotelDTO.getPersonList()));
            hotel.setHotelAddress(hotelDTO.getAddress());
            hotel.setHotelName(hotelDTO.getName());
            return hotel;
        }
    }

  	// 第二層映射
    public Hotel.Master toList(HotelDTO.Person person) {
        if (person == null) {
            return null;
        } else {
            Hotel.Master master = new Hotel.Master();
            master.setName(person.getPersonName());
            master.setAge(person.getPersonAge());
            return master;
        }
    }

  	// 調(diào)用第二層映射,將person集合的元素添加到master中
    protected List<Hotel.Master> personListToMasterList(List<HotelDTO.Person> list) {
        if (list == null) {
            return null;
        } else {
            List<Hotel.Master> list1 = new ArrayList(list.size());
            Iterator var3 = list.iterator();

            while(var3.hasNext()) {
                HotelDTO.Person person = (HotelDTO.Person)var3.next();
                list1.add(this.toList(person));
            }

            return list1;
        }
    }
}

4.字段的邏輯處理

4.1.復(fù)雜邏輯處理(qualifiedByName和@Named)

這次我們需要把dto中的personName和personAge的list集合映射到實(shí)體類(lèi)的masters集合中。常規(guī)的集合映射無(wú)法處理這種情況,這時(shí)需要使用到qualifiedByName和@Named進(jìn)行特殊處理。

@Data
@Builder
public class HotelDTO {
    // 酒店名稱(chēng)
    private String name;

    // 酒店地址
    private String address;

    private List<String> personName;

    private List<Integer> personAge;

}
@Data
public class Hotel {
    // 酒店名稱(chēng)
    private String hotelName;

    // 酒店地址
    private String hotelAddress;

    // 主人
    private List<Master> masters;

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Master {
        private String personName;

        private Integer personAge;
    }
}

這就需要拿到兩個(gè)list的數(shù)據(jù),進(jìn)行手動(dòng)處理了。在@Mapping注解的qualifiedByName屬性指定方法名定位處理邏輯的方法,@Named(“dtoToMasters”)。

利用stream流進(jìn)行處理。

// 酒店詳情
@Mappings({
        @Mapping(target = "hotelName", source = "name"),
        @Mapping(target = "hotelAddress", source = "address"),
        @Mapping(target = "masters", source = "hotelDTO", qualifiedByName = "dtoToMasters")
})
public abstract Hotel dto2Hotel(HotelDTO hotelDTO);

@Named("dtoToMasters")
List<Hotel.Master> dtoToMasters(HotelDTO hotelDTO) {
    List<String> personNames = hotelDTO.getPersonName();
    List<Integer> personAges = hotelDTO.getPersonAge();

    if (personNames != null && personAges != null && personNames.size() == personAges.size()) {
        return IntStream.range(0, personNames.size())
                .mapToObj(i -> new Hotel.Master(personNames.get(i), personAges.get(i)))
                .collect(Collectors.toList());
    }

    // 如果列表長(zhǎng)度不匹配或其他錯(cuò)誤情況,可以返回空列表或拋出異常
    return Collections.emptyList();
}

返回結(jié)果:

4.2.額外邏輯處理(ignore和@AfterMapping)

@Mappings的ignore屬性,也可以對(duì)一個(gè)字段(不能是集合)進(jìn)行額外邏輯處理。通常搭配@AfterMapping注解使用。

這個(gè)案例中,我們需要根據(jù)DTO的mount屬性判斷是否大于15,如果大于,則判斷hotel實(shí)體類(lèi)的isSuccess為true

@Data
public class Hotel {
    // 酒店名稱(chēng)
    private String hotelName;

    // 酒店地址
    private String hotelAddress;

    // 酒店生意是否興隆
    private Boolean isSuccess;
}
@Data
@Builder
public class HotelDTO {
    // 酒店名稱(chēng)
    private String name;

    // 酒店地址
    private String address;

    private Integer mount;
}

編寫(xiě)converter,注意@AfterMapping注解下的方法的參數(shù)列表,需要使用@MappingTarget注解指明目標(biāo)對(duì)象,

// 酒店詳情
@Mappings({
        @Mapping(target = "hotelName", source = "name"),
        @Mapping(target = "hotelAddress", source = "address"),
        @Mapping(target = "isSuccess", ignore = true)
})
public abstract Hotel dto2Hotel(HotelDTO hotelDTO);

@AfterMapping
void isSuccess(HotelDTO hotelDTO, @MappingTarget Hotel hotel) {
    if (hotelDTO.getMount() == null) {
        return;
    }
    boolean b = hotelDTO.getMount() > 15;
    hotel.setIsSuccess(b);
}

測(cè)試方法

@Test
public void Test() {
    HotelDTO build = HotelDTO.builder()
            .name("五星級(jí)酒店")
            .address("中國(guó)")
            .mount(18).build();

    TestConverter converter = new TestConverterImpl();

    Hotel hotel = converter.dto2Hotel(build);

    System.out.println(hotel);
}

返回結(jié)果

4.3.簡(jiǎn)單邏輯處理(expression)

expression可以在注解中編寫(xiě)簡(jiǎn)單的處理邏輯

在這個(gè)案例中我需要在實(shí)體類(lèi)的nowTime字段獲取當(dāng)前時(shí)間。

@Data
public class Hotel {
    // 酒店名稱(chēng)
    private String hotelName;

    // 酒店地址
    private String hotelAddress;

    private LocalDateTime nowTime;
}

直接在expression屬性中使用方法獲取當(dāng)前時(shí)間。

// 酒店詳情
@Mappings({
        @Mapping(target = "hotelName", source = "name"),
        @Mapping(target = "hotelAddress", source = "address"),
        @Mapping(expression = "java(java.time.LocalDateTime.now())", target = "nowTime")
})
public abstract Hotel dto2Hotel(HotelDTO hotelDTO);

結(jié)果如下

到此這篇關(guān)于SpringBoot中MapStruct實(shí)現(xiàn)優(yōu)雅的數(shù)據(jù)復(fù)制的文章就介紹到這了,更多相關(guān)SpringBoot MapStruct數(shù)據(jù)復(fù)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot:接收date類(lèi)型的參數(shù)方式

    springboot:接收date類(lèi)型的參數(shù)方式

    這篇文章主要介紹了springboot:接收date類(lèi)型的參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java?中的?getDeclaredFields()使用與原理解析

    Java?中的?getDeclaredFields()使用與原理解析

    在Java反射機(jī)制中,getDeclaredFields()用于獲取類(lèi)的所有字段,包括私有字段,通過(guò)反射,可以在運(yùn)行時(shí)動(dòng)態(tài)地獲取類(lèi)的信息并操作其成員,本文詳細(xì)介紹了getDeclaredFields()的使用方法、工作原理以及最佳實(shí)踐,涵蓋了反射的基本概念、使用場(chǎng)景和注意事項(xiàng),感興趣的朋友一起看看吧
    2025-01-01
  • Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(61)

    Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(61)

    下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你
    2021-08-08
  • Java多線程 樂(lè)觀鎖和CAS機(jī)制詳細(xì)

    Java多線程 樂(lè)觀鎖和CAS機(jī)制詳細(xì)

    這篇文章主要介紹了Java多線程 樂(lè)觀鎖和CAS機(jī)制,樂(lè)觀鎖是對(duì)于數(shù)據(jù)沖突保持一種樂(lè)觀態(tài)度,操作數(shù)據(jù)時(shí)不會(huì)對(duì)操作的數(shù)據(jù)進(jìn)行加鎖,需要的朋友可以參考下
    2021-10-10
  • java實(shí)現(xiàn)數(shù)字轉(zhuǎn)大寫(xiě)的方法

    java實(shí)現(xiàn)數(shù)字轉(zhuǎn)大寫(xiě)的方法

    這篇文章主要介紹了 java實(shí)現(xiàn)數(shù)字轉(zhuǎn)大寫(xiě)的方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-10-10
  • Java數(shù)據(jù)結(jié)構(gòu)與算法學(xué)習(xí)之循環(huán)鏈表

    Java數(shù)據(jù)結(jié)構(gòu)與算法學(xué)習(xí)之循環(huán)鏈表

    循環(huán)鏈表是另一種形式的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)。它的特點(diǎn)是表中最后一個(gè)結(jié)點(diǎn)的指針域指向頭結(jié)點(diǎn),整個(gè)鏈表形成一個(gè)環(huán)。本文將為大家詳細(xì)介紹一下循環(huán)鏈表的特點(diǎn)與使用,需要的可以了解一下
    2021-12-12
  • Java?時(shí)間相減算法題解示例

    Java?時(shí)間相減算法題解示例

    這篇文章主要為大家介紹了Java?時(shí)間相減算法題解示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 關(guān)于Spring MVC在Controller層中注入request的坑詳解

    關(guān)于Spring MVC在Controller層中注入request的坑詳解

    這篇文章主要給大家介紹了關(guān)于Spring MVC在Controller層中注入request的坑的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • JAVA操作HDFS案例的簡(jiǎn)單實(shí)現(xiàn)

    JAVA操作HDFS案例的簡(jiǎn)單實(shí)現(xiàn)

    本篇文章主要介紹了JAVA操作HDFS案例的簡(jiǎn)單實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • Java線程池高頻面試題總結(jié)

    Java線程池高頻面試題總結(jié)

    在進(jìn)程和線程的相關(guān)面試題中還有一部分是關(guān)于多線程和線程池的,也是在這一部分中比較??疾斓膬?nèi)容。本篇文章就帶你了解一下,希望能給你帶來(lái)幫助
    2021-08-08

最新評(píng)論