使用MapStruct實(shí)現(xiàn)Java對(duì)象映射的示例代碼
在分層架構(gòu)的開(kāi)發(fā)中,經(jīng)常遇到以下場(chǎng)景:將 DAO 查詢出的 Entity 對(duì)象,轉(zhuǎn)換為返回給前端的 VO 對(duì)象;或者將前端傳來(lái)的 DTO 對(duì)象,轉(zhuǎn)換為要存入數(shù)據(jù)庫(kù)的 Entity 對(duì)象。
每當(dāng)兩個(gè)對(duì)象的字段很多時(shí),用get和set的方法完成“數(shù)據(jù)搬運(yùn)”的過(guò)程就比較麻煩,接下來(lái)介紹一下使用MapStruct的方法。
一、什么是 MapStruct?
MapStruct 是一個(gè) Java 注解處理器,用于在編譯時(shí)自動(dòng)生成類(lèi)型安全、高性能的對(duì)象映射代碼。 請(qǐng)注意這里的幾個(gè)關(guān)鍵詞:
編譯時(shí)生成代碼:它不是像 `BeanUtils.copyProperties` 那樣在運(yùn)行時(shí)使用反射,所以性能極高,與你手寫(xiě)的 `get/set` 代碼完全一樣。
類(lèi)型安全:它在編譯期間就會(huì)對(duì)字段類(lèi)型進(jìn)行檢查。如果源對(duì)象和目標(biāo)對(duì)象的字段類(lèi)型不兼容,編譯會(huì)直接報(bào)錯(cuò),讓你能盡早發(fā)現(xiàn)問(wèn)題。
注解驅(qū)動(dòng):你只需要定義一個(gè)簡(jiǎn)單的 Java 接口并添加注解,MapStruct 就會(huì)在幕后為你完成所有繁重的工作。
二、實(shí)戰(zhàn)演練:三步集成 MapStruct
讓我們通過(guò)一個(gè)將 `DataProductItemDto` (DTO) 轉(zhuǎn)換為 `DataProduct` (Entity) 的實(shí)例,來(lái)看看 MapStruct 的使用方法。
第一步:添加 Maven 依賴 (`pom.xml`)
這是整個(gè)集成的基礎(chǔ)。需要告訴 Maven 項(xiàng)目要使用 MapStruct,并配置好編譯器。
<!-- 1. 在 <properties> 中統(tǒng)一定義版本號(hào) -->
<properties>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<lombok.version>1.18.30</lombok.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
<!-- 2. 在 <dependencies> 中添加核心庫(kù) -->
<dependencies>
<!-- MapStruct 核心庫(kù) -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!-- (如果使用Lombok) 這是讓 MapStruct 和 Lombok 和諧共處的關(guān)鍵 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</dependency>
</dependencies>
<!-- 3. 在 <build><plugins> 中配置注解處理器 (最關(guān)鍵的部分) -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- 或你的 Java 版本 -->
<target>1.8</target> <!-- 或你的 Java 版本 -->
<annotationProcessorPaths>
<!-- 必須確保 Lombok 在 MapStruct 之前執(zhí)行 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>配置要點(diǎn):annotationProcessorPaths 里的順序非常重要,確保了 Lombok 先為你的類(lèi)生成 getter/setter,然后 MapStruct 才能找到并使用它們。
修改完 pom.xml 后,務(wù)必重新構(gòu)建你的 Maven 項(xiàng)目
第二步:創(chuàng)建 Mapper 接口
現(xiàn)在需要定義“翻譯規(guī)則”。你只需要?jiǎng)?chuàng)建一個(gè)接口,剩下的交給 MapStruct。
package com.cmgii.trust.third.dtcp.converter;// 建議放在專(zhuān)門(mén)的 converter 包下
import com.cmgii.trust.third.dtcp.domain.DataProduct;
import com.cmgii.trust.third.dtcp.domain.vo.respVO.DataProductItemDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface DataProductConverter {
//提供一個(gè)全局唯一的翻譯官實(shí)例
DataProductConverter INSTANCE = Mappers.getMapper(DataProductConverter.class);
/**
* 將單個(gè) DTO 轉(zhuǎn)換為實(shí)體。
* MapStruct 會(huì)自動(dòng)映射所有同名、同類(lèi)型的字段。
* 我們只需為有特殊轉(zhuǎn)換需求的字段(如日期格式)添加規(guī)則。
* @param dto 源 DTO 對(duì)象
* @return 轉(zhuǎn)換后的實(shí)體對(duì)象
*/
@Mapping(source = "listTime", target = "listTime", dateFormat = "yyyy-MM-dd'T'HH:mm:ss")
DataProduct toDataProduct(DataProductItemDto dto);
/**
* 將 DTO 列表轉(zhuǎn)換為實(shí)體列表。
* MapStruct 會(huì)自動(dòng)復(fù)用上面的 toEntity 方法來(lái)處理列表中的每一個(gè)元素。
*
* @param dtoList 源 DTO 列表
* @return 轉(zhuǎn)換后的實(shí)體列表
*/
List<DataProduct> toDataProductList(List<DataProductItemDto> dtoList);
} 1. @Mapper 注解
作用:這是 MapStruct 的核心注解。它告訴 MapStruct 的代碼生成器:“嘿,這個(gè)接口不是一個(gè)普通的接口,它是一份映射規(guī)則的藍(lán)圖。請(qǐng)?jiān)诰幾g時(shí),為我自動(dòng)生成一個(gè)實(shí)現(xiàn)了這個(gè)接口的 ...Impl 類(lèi)。”
2. DataProductConverter INSTANCE = ... (單例模式)
- 作用:這是獲取 Mapper 實(shí)例的經(jīng)典方式(當(dāng)不使用 Spring 注入時(shí))。
- Mappers.getMapper(...): 這是 MapStruct 提供的一個(gè)工廠方法,它會(huì)找到并返回 MapStruct 在編譯時(shí)為您自動(dòng)生成的那個(gè) DataProductConverterImpl 類(lèi)的單例實(shí)例。
- 用法:在應(yīng)用的任何地方,都可以通過(guò) DataProductConverter.INSTANCE 來(lái)獲取到這個(gè)唯一的、線程安全的“翻譯官”,然后調(diào)用他的翻譯方法。
3. toDataProduct(DataProductItemDto dto) - 單個(gè)對(duì)象映射
- 作用:定義了如何將一個(gè) DataProductItemDto 對(duì)象,轉(zhuǎn)換成一個(gè) DataProduct 對(duì)象。
- MapStruct 的智能自動(dòng)化:DataProductItemDto 和 DataProduct 這兩個(gè)類(lèi)有大量同名、同類(lèi)型的字段。MapStruct 會(huì)自動(dòng)識(shí)別并映射這些字段,無(wú)需為它們編寫(xiě)任何 @Mapping 注解。
- @Mapping(...) - 特殊規(guī)則定制:
- source = "listTime", target = "listTime": source 指源對(duì)象(DTO)的屬性,target 指目標(biāo)對(duì)象(Domain)的屬性。這里兩者同名。
- dateFormat = "yyyy-MM-dd'T'HH:mm:ss": 這是最關(guān)鍵的定制規(guī)則。它告訴 MapStruct:“當(dāng)你在轉(zhuǎn)換 listTime 這個(gè)字段時(shí),請(qǐng)注意,源對(duì)象里的 listTime 是一個(gè)字符串,而目標(biāo)對(duì)象里的是一個(gè) Date。請(qǐng)你使用 "yyyy-MM-dd'T'HH:mm:ss" 這個(gè)格式,將字符串解析成 Date 對(duì)象。”
- 這完美地解決了日期格式解析問(wèn)題,而且是以一種非常聲明式、非常優(yōu)雅的方式。
4. toDataProductList(List<DataProductItemDto> dtoList) - 集合映射
- 作用:定義了如何將一個(gè) DataProductItemDto 的列表,轉(zhuǎn)換成一個(gè) DataProduct 的列表。
- MapStruct 的進(jìn)一步智能:MapStruct 足夠智能,當(dāng)它看到這個(gè)方法時(shí),它會(huì)發(fā)現(xiàn):“哦,我知道如何轉(zhuǎn)換單個(gè) DataProductItemDto(通過(guò) toDataProduct 方法),那轉(zhuǎn)換一個(gè)列表,我只需要對(duì)列表里的每個(gè)元素都調(diào)用一次 toDataProduct 方法就行了。”
- 因此,不需要為這個(gè)方法編寫(xiě)任何額外的 @Mapping 注解。MapStruct 會(huì)自動(dòng)生成一個(gè)循環(huán)遍歷并逐個(gè)轉(zhuǎn)換的實(shí)現(xiàn)。
第三步:在 Service 中使用 Mapper
可以在業(yè)務(wù)邏輯中,用一行代碼替代原來(lái)繁瑣的轉(zhuǎn)換過(guò)程。
@Override
@Transactional
public void syncDataProducts() {
// 1. 從第三方獲取所有數(shù)據(jù)產(chǎn)品
List<DataProductItemDto> remoteProductsDto = cmccDtcpThirdService.selectAllDataProductList();
// 2. 將DTO轉(zhuǎn)換為實(shí)體
List<DataProduct> dataProducts = DataProductConverter.INSTANCE.toDataProductList(remoteProductsDto);
// 3. 批量存入數(shù)據(jù)庫(kù)
if (dataProducts != null && !dataProducts.isEmpty()) {
dataProductMapper.batchInsertOrUpdate(dataProducts);
}
}三、總結(jié):為什么選擇 MapStruct?
| 特性 | 手動(dòng) get/set | BeanUtils.copyProperties | MapStruct |
| 性能 | 極高 | 較低 (運(yùn)行時(shí)反射) | 極高 (編譯時(shí)生成) |
| 類(lèi)型安全 | 編譯時(shí) | 運(yùn)行時(shí) (可能沉默失敗) | 編譯時(shí) (推薦) |
| 代碼簡(jiǎn)潔度 | 差 | 好 | 極好 |
| 可維護(hù)性 | 差 | 中 | 極好 |
到此這篇關(guān)于使用MapStruct實(shí)現(xiàn)Java對(duì)象映射的文章就介紹到這了,更多相關(guān)MapStruct 實(shí)現(xiàn) Java 對(duì)象映射內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Java實(shí)現(xiàn)裁剪和壓縮PPT中的圖片
在工作中,PPT演示文稿廣泛應(yīng)用于報(bào)告、課程內(nèi)容展示以及項(xiàng)目計(jì)劃,然而,當(dāng)?PPT?文件中包含大量圖片時(shí),文件大小常常大幅增加,導(dǎo)致加載緩慢、分享困難等問(wèn)題,因此在這篇文章中,我們將詳細(xì)介紹如何使用Java實(shí)現(xiàn)裁剪和壓縮PPT中的圖片,需要的朋友可以參考下2026-03-03
Java Web中常用的分頁(yè)組件(Java端實(shí)現(xiàn))
本文通過(guò)使用場(chǎng)景分析給大家介紹了Java Web中常用的分頁(yè)組件(Java端實(shí)現(xiàn)),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-05-05
解決JDBC Connection Reset的問(wèn)題分析
這篇文章主要介紹了解決JDBC Connection Reset的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
Java 正則表達(dá)式的使用實(shí)戰(zhàn)案例
本文詳細(xì)介紹了Java正則表達(dá)式的使用方法,涵蓋語(yǔ)法細(xì)節(jié)、核心類(lèi)方法、高級(jí)特性及實(shí)戰(zhàn)案例,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友跟隨小編一起看看吧2025-08-08
springboot如何連接兩個(gè)數(shù)據(jù)庫(kù)(多個(gè))
這篇文章主要介紹了springboot如何連接兩個(gè)數(shù)據(jù)庫(kù)(多個(gè)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01

