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

Java中MapStruct轉(zhuǎn)換實(shí)體的步驟實(shí)現(xiàn)

 更新時(shí)間:2026年03月09日 10:25:02   作者:天道佩恩  
本文主要介紹了Java中MapStruct轉(zhuǎn)換實(shí)體的步驟實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前幾天在代碼中看到有人用UnitChangeConvert.INSTANCE.dtoToBO(processVo),感覺(jué)非常帥。這只是一個(gè)接口,并沒(méi)有給出實(shí)現(xiàn)類(lèi),就給實(shí)體轉(zhuǎn)換了,省去了我們自己寫(xiě)轉(zhuǎn)換類(lèi)的時(shí)間,今天摸魚(yú)中了解了下:MapStruct 是一個(gè)專(zhuān)注于「Java Bean 之間屬性映射」的代碼生成器,與 Spring 原生轉(zhuǎn)換、MyBatis-Plus 類(lèi)型轉(zhuǎn)換器是完全不同的工具,解決的是「對(duì)象與對(duì)象之間的屬性拷貝」問(wèn)題(如 DTO ↔ POJO、VO ↔ Entity),而非參數(shù) / 數(shù)據(jù)庫(kù)字段的類(lèi)型轉(zhuǎn)換。

下面從 核心定位、與其他轉(zhuǎn)換工具的區(qū)別、使用場(chǎng)景、快速上手、高級(jí)特性 五個(gè)維度,幫你理清 MapStruct 的核心價(jià)值和用法:

一、核心定位:Java Bean 映射的 “代碼生成神器”

日常開(kāi)發(fā)中,我們經(jīng)常需要在不同層級(jí)的 Java Bean 之間拷貝屬性(如:

  • Controller 接收前端 UserDTO → 服務(wù)層轉(zhuǎn)換為 UserEntity 存入數(shù)據(jù)庫(kù);
  • 數(shù)據(jù)庫(kù)查詢(xún)出 UserEntity → 轉(zhuǎn)換為 UserVO 返回給前端)。

傳統(tǒng)做法是手動(dòng)寫(xiě) setter 拷貝(繁瑣易出錯(cuò)),或用 BeanUtils.copyProperties(反射效率低、字段名不一致需手動(dòng)處理、類(lèi)型轉(zhuǎn)換不靈活)。

MapStruct 的核心解決思路:

  • 基于「注解 + 接口」定義映射規(guī)則;
  • 編譯期自動(dòng)生成 類(lèi)型安全、無(wú)反射、高效 的映射實(shí)現(xiàn)類(lèi)(純 Java 代碼,不是運(yùn)行時(shí)動(dòng)態(tài)處理);
  • 支持字段名映射、類(lèi)型自動(dòng)轉(zhuǎn)換、自定義轉(zhuǎn)換邏輯、依賴(lài)注入等。

二、與其他 “轉(zhuǎn)換工具” 的核心區(qū)別

你之前接觸的工具和 MapStruct 適用場(chǎng)景完全不同,用表格清晰對(duì)比:

工具 / 框架核心用途適用場(chǎng)景底層實(shí)現(xiàn)
MapStructJava Bean ↔ Java Bean(屬性拷貝)DTO↔Entity、VO↔POJO(業(yè)務(wù)層)編譯期生成 Java 代碼(無(wú)反射)
Spring 原生 Converter基礎(chǔ)類(lèi)型 / 簡(jiǎn)單對(duì)象轉(zhuǎn)換(如 String→Integer)Web 參數(shù)轉(zhuǎn)換、配置注入(Web / 配置層)接口實(shí)現(xiàn) + Spring 容器管理
MyBatis-Plus 轉(zhuǎn)換器數(shù)據(jù)庫(kù)字段 ↔ Java 實(shí)體(ORM 層)數(shù)據(jù)庫(kù)存儲(chǔ)類(lèi)型→Java 類(lèi)型MyBatis 類(lèi)型處理器接口
BeanUtils(Spring/Commons)快速屬性拷貝簡(jiǎn)單場(chǎng)景(字段名完全一致)運(yùn)行時(shí)反射(效率低)

一句話區(qū)分:

  • 要拷貝兩個(gè) Java Bean 的屬性 → 用 MapStruct;
  • 要把 String 轉(zhuǎn) Integer / 枚舉(非 Bean 拷貝) → 用 Spring Converter;
  • 要把數(shù)據(jù)庫(kù)字段轉(zhuǎn) Java 實(shí)體 → 用 MyBatis-Plus 轉(zhuǎn)換器。

三、MapStruct 快速上手(Spring Boot 環(huán)境)

步驟 1:引入依賴(lài)(Maven)

<!-- MapStruct 核心依賴(lài) -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version> <!-- 推薦穩(wěn)定版 -->
</dependency>
?
<!-- 編譯期代碼生成依賴(lài)(必須,否則不會(huì)生成實(shí)現(xiàn)類(lèi)) -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
    <scope>provided</scope>
</dependency>
?
<!-- 若用 Lombok,需確保 MapStruct 能識(shí)別 Lombok 生成的 getter/setter -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>
?
<!-- Lombok 與 MapStruct 兼容依賴(lài)(Java 11+ 可能需要) -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok-mapstruct-binding</artifactId>
    <version>0.2.0</version>
    <scope>provided</scope>
</dependency>

步驟 2:定義待映射的 Java Bean

假設(shè)存在「用戶(hù)實(shí)體」和「用戶(hù) DTO」(字段名部分不一致,類(lèi)型不同):

// 數(shù)據(jù)庫(kù)實(shí)體(POJO)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
    private Long id;          // 主鍵
    private String userName;  // 用戶(hù)名(字段名:userName)
    private Integer age;      // 年齡(int 類(lèi)型)
    private LocalDate birth;  // 生日(LocalDate 類(lèi)型)
    private String phone;     // 手機(jī)號(hào)
}
?
// 前端傳輸對(duì)象(DTO)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
    private Long userId;      // 主鍵(字段名:userId,與實(shí)體 id 對(duì)應(yīng))
    private String name;      // 用戶(hù)名(字段名:name,與實(shí)體 userName 對(duì)應(yīng))
    private String age;       // 年齡(String 類(lèi)型,與實(shí)體 int 對(duì)應(yīng))
    private String birth;     // 生日(String 類(lèi)型,格式:yyyy-MM-dd)
    private String phoneNum;  // 手機(jī)號(hào)(字段名:phoneNum,與實(shí)體 phone 對(duì)應(yīng))
}

步驟 3:定義 MapStruct 映射接口

@Mapper 注解(MapStruct 的注解,非 MyBatis 的 @Mapper?。┒x映射規(guī)則,componentModel = "spring" 表示讓 Spring 管理映射器實(shí)例(支持依賴(lài)注入):

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
?
// 關(guān)鍵注解:componentModel="spring" → Spring 管理該映射器
@Mapper(componentModel = "spring", imports = {LocalDate.class, DateTimeFormatter.class})
public interface UserConvert {
?
    // 方式 1:Spring 依賴(lài)注入用(無(wú)需手動(dòng)創(chuàng)建實(shí)例)
    // 方式 2:非 Spring 環(huán)境用(手動(dòng)獲取實(shí)例)
    // UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
?
    /**
     * DTO → 實(shí)體(核心映射方法)
     * @Mapping:指定字段映射規(guī)則(字段名不一致、類(lèi)型轉(zhuǎn)換、格式轉(zhuǎn)換)
     */
    @Mappings({
        // DTO 的 userId → 實(shí)體的 id(字段名不一致)
        @Mapping(source = "userId", target = "id"),
        // DTO 的 name → 實(shí)體的 userName(字段名不一致)
        @Mapping(source = "name", target = "userName"),
        // DTO 的 age(String)→ 實(shí)體的 age(int):自動(dòng)類(lèi)型轉(zhuǎn)換(MapStruct 內(nèi)置支持)
        @Mapping(source = "age", target = "age"),
        // DTO 的 birth(String)→ 實(shí)體的 birth(LocalDate):自定義格式轉(zhuǎn)換
        @Mapping(
            source = "birth",
            target = "birth",
            dateFormat = "yyyy-MM-dd" // 指定日期格式
        ),
        // DTO 的 phoneNum → 實(shí)體的 phone(字段名不一致)
        @Mapping(source = "phoneNum", target = "phone")
    })
    UserEntity dtoToEntity(UserDTO userDTO);
?
    /**
     * 實(shí)體 → DTO(反向映射,字段名對(duì)應(yīng)規(guī)則與正向一致)
     * 可復(fù)用正向映射規(guī)則,用 @InheritInverseConfiguration 簡(jiǎn)化
     */
    @InheritInverseConfiguration(name = "dtoToEntity")
    @Mapping(
        source = "birth",
        target = "birth",
        dateFormat = "yyyy-MM-dd" // 反向轉(zhuǎn)換也需指定日期格式
    )
    UserDTO entityToDto(UserEntity userEntity);
?
    /**
     * 自定義轉(zhuǎn)換邏輯(如特殊格式處理、復(fù)雜計(jì)算)
     * 若 MapStruct 內(nèi)置轉(zhuǎn)換不滿(mǎn)足,可定義默認(rèn)方法(default)
     */
    default Integer stringToAge(String ageStr) {
        if (ageStr == null || ageStr.trim().isEmpty()) {
            return 0; // 空值默認(rèn) 0
        }
        try {
            return Integer.parseInt(ageStr.trim());
        } catch (NumberFormatException e) {
            return 0; // 格式錯(cuò)誤默認(rèn) 0
        }
    }
?
    // 反向轉(zhuǎn)換:Integer → String
    default String ageToString(Integer age) {
        return age == null ? "" : String.valueOf(age);
    }
}

步驟 4:編譯項(xiàng)目,查看生成的實(shí)現(xiàn)類(lèi)

MapStruct 會(huì)在 編譯期 生成 UserConvertImpl 實(shí)現(xiàn)類(lèi)(無(wú)需手動(dòng)寫(xiě)),路徑在 target/generated-sources/annotations/ 下,核心邏輯如下(自動(dòng)生成的純 Java 代碼):

// 自動(dòng)生成的實(shí)現(xiàn)類(lèi)(Spring 管理)
@Component
public class UserConvertImpl implements UserConvert {
?
    @Override
    public UserEntity dtoToEntity(UserDTO userDTO) {
        if (userDTO == null) {
            return null;
        }
        UserEntity userEntity = new UserEntity();
        userEntity.setId(userDTO.getUserId());
        userEntity.setUserName(userDTO.getName());
        // 調(diào)用自定義的 stringToAge 方法轉(zhuǎn)換類(lèi)型
        userEntity.setAge(stringToAge(userDTO.getAge()));
        // 自動(dòng)按指定格式轉(zhuǎn)換 String → LocalDate
        if (userDTO.getBirth() != null) {
            userEntity.setBirth(LocalDate.parse(userDTO.getBirth(), DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        }
        userEntity.setPhone(userDTO.getPhoneNum());
        return userEntity;
    }
?
    @Override
    public UserDTO entityToDto(UserEntity userEntity) {
        // 類(lèi)似正向轉(zhuǎn)換邏輯,自動(dòng)處理字段映射和類(lèi)型轉(zhuǎn)換
    }
?
    // 自動(dòng)生成自定義方法的調(diào)用邏輯...
}

步驟 5:在 Spring 中使用映射器

通過(guò) @Autowired 注入 UserConvert,直接調(diào)用映射方法:

@Service
public class UserService {
?
    @Autowired
    private UserConvert userConvert; // 注入 MapStruct 映射器
?
    public void addUser(UserDTO userDTO) {
        // 1. DTO → 實(shí)體(自動(dòng)完成字段映射和類(lèi)型轉(zhuǎn)換)
        UserEntity userEntity = userConvert.dtoToEntity(userDTO);
        
        // 2. 存入數(shù)據(jù)庫(kù)(假設(shè)用 MyBatis 操作)
        // userMapper.insert(userEntity);
        
        System.out.println("轉(zhuǎn)換后的實(shí)體:" + userEntity);
    }
?
    public UserDTO getUserById(Long id) {
        // 1. 從數(shù)據(jù)庫(kù)查詢(xún)實(shí)體
        UserEntity userEntity = new UserEntity(id, "張三", 25, LocalDate.of(2000, 1, 1), "13800138000");
        
        // 2. 實(shí)體 → DTO(反向轉(zhuǎn)換)
        UserDTO userDTO = userConvert.entityToDto(userEntity);
        
        return userDTO;
    }
}

測(cè)試效果:

// 測(cè)試 DTO → 實(shí)體
UserDTO dto = new UserDTO(1L, "張三", "25", "2000-01-01", "13800138000");
UserEntity entity = userConvert.dtoToEntity(dto);
// 輸出:UserEntity(id=1, userName=張三, age=25, birth=2000-01-01, phone=13800138000)
?
// 測(cè)試實(shí)體 → DTO
UserEntity entity = new UserEntity(1L, "張三", 25, LocalDate.of(2000, 1, 1), "13800138000");
UserDTO dto = userConvert.entityToDto(entity);
// 輸出:UserDTO(userId=1, name=張三, age=25, birth=2000-01-01, phoneNum=13800138000)

四、MapStruct 核心注解與高級(jí)特性

1. 核心注解(常用)

注解作用示例
@Mapper標(biāo)識(shí)映射接口,指定組件模型(如 Spring)@Mapper(componentModel = "spring")
@Mapping單個(gè)字段映射規(guī)則@Mapping(source = "name", target = "userName")
@Mappings多個(gè) @Mapping 的集合包裹多個(gè) @Mapping 注解
@InheritInverseConfiguration繼承反向映射規(guī)則(避免重復(fù)寫(xiě)注解)@InheritInverseConfiguration(name = "dtoToEntity")
@MappingTarget映射到已存在的對(duì)象(更新屬性)void updateEntity(UserDTO dto, @MappingTarget UserEntity entity)
@Named命名自定義轉(zhuǎn)換方法(用于復(fù)雜映射)@Named("stringToAge") default Integer stringToAge(String s) {}

2. 高級(jí)特性

(1)集合映射(自動(dòng)支持 List/Set/Array)

MapStruct 會(huì)自動(dòng)為集合生成映射方法,無(wú)需手動(dòng)定義:

@Mapper(componentModel = "spring")
public interface UserConvert {
    // 單個(gè)對(duì)象映射(已定義)
    UserEntity dtoToEntity(UserDTO dto);
    UserDTO entityToDto(UserEntity entity);
?
    // 集合映射(自動(dòng)生成,無(wú)需手動(dòng)寫(xiě)實(shí)現(xiàn))
    List<UserEntity> dtoListToEntityList(List<UserDTO> dtoList);
    Set<UserDTO> entitySetToDtoSet(Set<UserEntity> entitySet);
}

(2)自定義轉(zhuǎn)換邏輯(復(fù)雜場(chǎng)景)

若內(nèi)置轉(zhuǎn)換不滿(mǎn)足(如枚舉轉(zhuǎn)換、對(duì)象嵌套),可定義 default 方法或單獨(dú)的轉(zhuǎn)換類(lèi):

// 示例:枚舉轉(zhuǎn)換
public enum GenderEnum {
    MALE(1, "男"), FEMALE(2, "女");
    // getter/setter/靜態(tài)方法...
}
?
// DTO 中 gender 是 String 類(lèi)型("男"/"女"),實(shí)體中是 GenderEnum
@Mapping(
    source = "gender",
    target = "gender",
    qualifiedByName = "genderStrToEnum" // 指定自定義轉(zhuǎn)換方法
)
UserEntity dtoToEntity(UserDTO dto);
?
// 自定義枚舉轉(zhuǎn)換方法(用 @Named 標(biāo)識(shí))
@Named("genderStrToEnum")
default GenderEnum genderStrToEnum(String genderStr) {
    if ("男".equals(genderStr)) return GenderEnum.MALE;
    if ("女".equals(genderStr)) return GenderEnum.FEMALE;
    return null;
}

(3)依賴(lài)注入其他服務(wù)

componentModel = "spring",映射器可注入 Spring Bean(如業(yè)務(wù)服務(wù)、工具類(lèi)):

@Mapper(componentModel = "spring")
public interface UserConvert {
?
    @Autowired
    UserService userService; // 注入 Spring 服務(wù)
?
    @Mapping(
        source = "userId",
        target = "deptId",
        qualifiedByName = "getDeptIdByUserId" // 調(diào)用注入的服務(wù)
    )
    UserEntity dtoToEntity(UserDTO dto);
?
    @Named("getDeptIdByUserId")
    default Long getDeptIdByUserId(Long userId) {
        // 調(diào)用業(yè)務(wù)服務(wù)獲取部門(mén)ID(復(fù)雜邏輯)
        return userService.getDeptIdByUserId(userId);
    }
}

(4)日期 / 數(shù)字格式轉(zhuǎn)換

通過(guò) dateFormat/numberFormat 指定格式:

// 日期格式
@Mapping(source = "birth", target = "birth", dateFormat = "yyyy-MM-dd HH:mm:ss")
// 數(shù)字格式(如保留2位小數(shù))
@Mapping(source = "amount", target = "amount", numberFormat = "#.00")
UserEntity dtoToEntity(UserDTO dto);

五、注意事項(xiàng)

  1. 編譯期生成代碼

    • 必須引入 mapstruct-processor 依賴(lài),否則不會(huì)生成實(shí)現(xiàn)類(lèi),運(yùn)行時(shí)會(huì)報(bào)「找不到實(shí)現(xiàn)類(lèi)」錯(cuò)誤;
    • 若用 IDEA,需開(kāi)啟「Annotation Processing」(Settings → Build → Compiler → Annotation Processors),否則 IDEA 可能不識(shí)別生成的類(lèi)。
  2. 與 Lombok 兼容

    • 確保 Lombok 版本 ≥ 1.18.16,MapStruct 版本 ≥ 1.4.2;
    • 若字段無(wú) getter/setter(如用 @Data 生成),MapStruct 無(wú)法識(shí)別字段,需確保 Lombok 正確生成訪問(wèn)器。
  3. 字段名匹配規(guī)則

    • 默認(rèn)按「字段名相同」映射(忽略大小寫(xiě)?不,嚴(yán)格匹配);
    • 若字段名是 userName(實(shí)體)和 user_name(DTO),可開(kāi)啟 unmappedTargetPolicy = ReportingPolicy.IGNORE 忽略未映射字段,或手動(dòng)指定 @Mapping。

總結(jié)

MapStruct 是 Java Bean 映射的最優(yōu)解,核心優(yōu)勢(shì):

  1. 類(lèi)型安全:編譯期檢查字段名、類(lèi)型是否匹配,避免運(yùn)行時(shí)錯(cuò)誤;
  2. 效率高:無(wú)反射,編譯期生成原生 Java 代碼,性能遠(yuǎn)超 BeanUtils;
  3. 靈活:支持字段映射、類(lèi)型轉(zhuǎn)換、自定義邏輯、依賴(lài)注入;
  4. 簡(jiǎn)化代碼:無(wú)需手動(dòng)寫(xiě) setter 拷貝,注解驅(qū)動(dòng)開(kāi)發(fā)。

到此這篇關(guān)于Java中MapStruct轉(zhuǎn)換實(shí)體的步驟實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java MapStruct轉(zhuǎn)換實(shí)體內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

穆棱市| 交城县| 邮箱| 盐边县| 吴川市| 涪陵区| 嘉义县| 方城县| 盐池县| 福安市| 兴国县| 周宁县| 格尔木市| 石屏县| 许昌市| 岢岚县| 子长县| 武城县| 黔东| 乌兰县| 昭平县| 教育| 寿阳县| 寿宁县| 安龙县| 涿州市| 株洲市| 甘孜县| 新竹市| 伊宁县| 时尚| 张家川| 伊吾县| 木里| 富蕴县| 年辖:市辖区| 牙克石市| 宁化县| 洪江市| 鄂州市| 太康县|