最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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ù)制,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

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

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

1.為什么選擇MapStruct

1.1.常見的屬性映射方法

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2.MapStruct快速入門

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

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

<!-- 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í)體類要具有g(shù)et/set方法,這里我使用了lombok的@Data注解來實(shí)現(xiàn)。

import lombok.Data;

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

    // 酒店地址
    private String hotelAddress;

    // 所在城市
    private String hotelCity;

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

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

import lombok.Builder;
import lombok.Data;

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

    // 酒店地址
    private String address;

    // 所在城市
    private String city;
}

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

使用抽象類來定義轉(zhuǎn)換器,只需中@Mapping注解中填寫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è)試類中測(cè)試,這里我使用DTO類的@Builder注解提供的方法為dto賦初值模擬實(shí)際開發(fā),通過調(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)類

里面為我們編寫好了映射的代碼。

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

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

3.1.嵌套映射

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

@Data
public class Hotel {
    // 酒店名稱
    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 {
    // 酒店名稱
    private String name;

    // 酒店地址
    private String address;

    // 所在城市
    private String city;

    private String personName;

    private Integer personAge;
}

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

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

//酒店詳情
@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ì)象的集合的元素類型都是基本數(shù)據(jù)類型,直接在target和source中填寫字段名即可。

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

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

@Data
@Builder
public class HotelDTO {
    // 酒店名稱
    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 {
    // 酒店名稱
    private String hotelName;

    // 酒店地址
    private String hotelAddress;

    private List<Master> masters;

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

        private Integer age;
    }
}

編寫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í)體類的masters集合中。常規(guī)的集合映射無法處理這種情況,這時(shí)需要使用到qualifiedByName和@Named進(jìn)行特殊處理。

@Data
@Builder
public class HotelDTO {
    // 酒店名稱
    private String name;

    // 酒店地址
    private String address;

    private List<String> personName;

    private List<Integer> personAge;

}
@Data
public class Hotel {
    // 酒店名稱
    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í)體類的isSuccess為true

@Data
public class Hotel {
    // 酒店名稱
    private String hotelName;

    // 酒店地址
    private String hotelAddress;

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

    // 酒店地址
    private String address;

    private Integer mount;
}

編寫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可以在注解中編寫簡(jiǎn)單的處理邏輯

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

@Data
public class Hotel {
    // 酒店名稱
    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)文章

  • Jenkins環(huán)境搭建實(shí)現(xiàn)過程圖解

    Jenkins環(huán)境搭建實(shí)現(xiàn)過程圖解

    這篇文章主要介紹了Jenkins環(huán)境搭建實(shí)現(xiàn)過程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Spring中的FactoryBean與ObjectFactory詳解

    Spring中的FactoryBean與ObjectFactory詳解

    這篇文章主要介紹了Spring中的FactoryBean與ObjectFactory詳解,FactoryBean是一種特殊的bean,本身又是個(gè)工廠,實(shí)現(xiàn)了FactoryBean的bean會(huì)被注冊(cè)到容器中,需要的朋友可以參考下
    2023-12-12
  • java中URLencode、URLdecode及Base64加解密轉(zhuǎn)換

    java中URLencode、URLdecode及Base64加解密轉(zhuǎn)換

    本文主要介紹了java中URLencode、URLdecode及Base64加解密轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • mac下idea啟動(dòng)web項(xiàng)目報(bào)錯(cuò)java.net.SocketException:socket closed問題

    mac下idea啟動(dòng)web項(xiàng)目報(bào)錯(cuò)java.net.SocketException:socket closed

    本文主要介紹了作者在項(xiàng)目啟動(dòng)時(shí)遇到的一個(gè)問題——無法打開調(diào)試端口,經(jīng)過一系列排查和嘗試,最終發(fā)現(xiàn)是由于權(quán)限問題導(dǎo)致的,作者還分享了如何修改文件權(quán)限的方法,并提醒大家不要隨意kill掉占用端口的進(jìn)程
    2024-12-12
  • mybatis教程之增刪改查_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    mybatis教程之增刪改查_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了mybatis教程之增刪改查,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • Java集合框架Collections原理及用法實(shí)例

    Java集合框架Collections原理及用法實(shí)例

    這篇文章主要介紹了Java集合框架Collections原理及用法實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • struts2獲取服務(wù)器臨時(shí)目錄的方法

    struts2獲取服務(wù)器臨時(shí)目錄的方法

    這篇文章主要為大家詳細(xì)介紹了struts2獲取服務(wù)器臨時(shí)目錄的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • Java在創(chuàng)建文件時(shí)指定編碼的實(shí)現(xiàn)方法

    Java在創(chuàng)建文件時(shí)指定編碼的實(shí)現(xiàn)方法

    本文主要介紹了Java在創(chuàng)建文件時(shí)指定編碼的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • VSCode?Java項(xiàng)目JDK配置完整攻略指南(JDK版本切換不求人)

    VSCode?Java項(xiàng)目JDK配置完整攻略指南(JDK版本切換不求人)

    JDK是指Java軟件開發(fā)工具包,是Java編程語言的官方實(shí)現(xiàn),由Oracle公司提供,不斷更新和維護(hù),這篇文章主要介紹了VSCode Java項(xiàng)目JDK配置(JDK版本切換不求人)的相關(guān)資料,需要的朋友可以參考下
    2026-05-05
  • Java Random 隨機(jī)數(shù)的用法小結(jié)

    Java Random 隨機(jī)數(shù)的用法小結(jié)

    本文主要介紹了Java Random隨機(jī)數(shù)的用法小結(jié),Random類功能全面,支持生成整數(shù)、浮點(diǎn)數(shù)、布爾值及實(shí)現(xiàn)洗牌等高級(jí)操作,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-07-07

最新評(píng)論

德昌县| 津南区| 东丽区| 祥云县| 盐津县| 平山县| 南昌县| 汝城县| 常熟市| 长沙县| 邵阳市| 礼泉县| 兴仁县| 大荔县| 桂林市| 莱阳市| 建平县| 正镶白旗| 上犹县| 新沂市| 讷河市| 油尖旺区| 桂林市| 廊坊市| 临澧县| 长治县| 双牌县| 呼图壁县| 阿坝| 汪清县| 湘乡市| 文山县| 遵义县| 武安市| 阿巴嘎旗| 莱州市| 教育| 台北县| 息烽县| 惠来县| 会昌县|