Java中MapStruct转换实体的步骤实现

 更新时间:2026年03月09日 10:25:02   作者:天道佩恩  
本文主要介绍了Java中MapStruct转换实体的步骤实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前几天在代码中看到有人用UnitChangeConvert.INSTANCE.dtoToBO(processVo),感觉非常帅。这只是一个接口,并没有给出实现类,就给实体转换了,省去了我们自己写转换类的时间,今天摸鱼中了解了下:MapStruct 是一个专注于「Java Bean 之间属性映射」的代码生成器,与 Spring 原生转换、MyBatis-Plus 类型转换器是完全不同的工具,解决的是「对象与对象之间的属性拷贝」问题(如 DTO ↔ POJO、VO ↔ Entity),而非参数 / 数据库字段的类型转换。

下面从 核心定位、与其他转换工具的区别、使用场景、快速上手、高级特性 五个维度,帮你理清 MapStruct 的核心价值和用法:

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

日常开发中,我们经常需要在不同层级的 Java Bean 之间拷贝属性(如:

  • Controller 接收前端 UserDTO → 服务层转换为 UserEntity 存入数据库;
  • 数据库查询出 UserEntity → 转换为 UserVO 返回给前端)。

传统做法是手动写 setter 拷贝(繁琐易出错),或用 BeanUtils.copyProperties(反射效率低、字段名不一致需手动处理、类型转换不灵活)。

MapStruct 的核心解决思路:

  • 基于「注解 + 接口」定义映射规则;
  • 编译期自动生成 类型安全、无反射、高效 的映射实现类(纯 Java 代码,不是运行时动态处理);
  • 支持字段名映射、类型自动转换、自定义转换逻辑、依赖注入等。

二、与其他 “转换工具” 的核心区别

你之前接触的工具和 MapStruct 适用场景完全不同,用表格清晰对比:

工具 / 框架核心用途适用场景底层实现
MapStructJava Bean ↔ Java Bean(属性拷贝)DTO↔Entity、VO↔POJO(业务层)编译期生成 Java 代码(无反射)
Spring 原生 Converter基础类型 / 简单对象转换(如 String→Integer)Web 参数转换、配置注入(Web / 配置层)接口实现 + Spring 容器管理
MyBatis-Plus 转换器数据库字段 ↔ Java 实体(ORM 层)数据库存储类型→Java 类型MyBatis 类型处理器接口
BeanUtils(Spring/Commons)快速属性拷贝简单场景(字段名完全一致)运行时反射(效率低)

一句话区分:

  • 要拷贝两个 Java Bean 的属性 → 用 MapStruct;
  • 要把 String 转 Integer / 枚举(非 Bean 拷贝) → 用 Spring Converter
  • 要把数据库字段转 Java 实体 → 用 MyBatis-Plus 转换器。

三、MapStruct 快速上手(Spring Boot 环境)

步骤 1:引入依赖(Maven)

<!-- MapStruct 核心依赖 -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version> <!-- 推荐稳定版 -->
</dependency>
​
<!-- 编译期代码生成依赖(必须,否则不会生成实现类) -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
    <scope>provided</scope>
</dependency>
​
<!-- 若用 Lombok,需确保 MapStruct 能识别 Lombok 生成的 getter/setter -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>
​
<!-- Lombok 与 MapStruct 兼容依赖(Java 11+ 可能需要) -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok-mapstruct-binding</artifactId>
    <version>0.2.0</version>
    <scope>provided</scope>
</dependency>

步骤 2:定义待映射的 Java Bean

假设存在「用户实体」和「用户 DTO」(字段名部分不一致,类型不同):

// 数据库实体(POJO)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
    private Long id;          // 主键
    private String userName;  // 用户名(字段名:userName)
    private Integer age;      // 年龄(int 类型)
    private LocalDate birth;  // 生日(LocalDate 类型)
    private String phone;     // 手机号
}
​
// 前端传输对象(DTO)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
    private Long userId;      // 主键(字段名:userId,与实体 id 对应)
    private String name;      // 用户名(字段名:name,与实体 userName 对应)
    private String age;       // 年龄(String 类型,与实体 int 对应)
    private String birth;     // 生日(String 类型,格式:yyyy-MM-dd)
    private String phoneNum;  // 手机号(字段名:phoneNum,与实体 phone 对应)
}

步骤 3:定义 MapStruct 映射接口

@Mapper 注解(MapStruct 的注解,非 MyBatis 的 @Mapper!)定义映射规则,componentModel = "spring" 表示让 Spring 管理映射器实例(支持依赖注入):

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
​
// 关键注解:componentModel="spring" → Spring 管理该映射器
@Mapper(componentModel = "spring", imports = {LocalDate.class, DateTimeFormatter.class})
public interface UserConvert {
​
    // 方式 1:Spring 依赖注入用(无需手动创建实例)
    // 方式 2:非 Spring 环境用(手动获取实例)
    // UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
​
    /**
     * DTO → 实体(核心映射方法)
     * @Mapping:指定字段映射规则(字段名不一致、类型转换、格式转换)
     */
    @Mappings({
        // DTO 的 userId → 实体的 id(字段名不一致)
        @Mapping(source = "userId", target = "id"),
        // DTO 的 name → 实体的 userName(字段名不一致)
        @Mapping(source = "name", target = "userName"),
        // DTO 的 age(String)→ 实体的 age(int):自动类型转换(MapStruct 内置支持)
        @Mapping(source = "age", target = "age"),
        // DTO 的 birth(String)→ 实体的 birth(LocalDate):自定义格式转换
        @Mapping(
            source = "birth",
            target = "birth",
            dateFormat = "yyyy-MM-dd" // 指定日期格式
        ),
        // DTO 的 phoneNum → 实体的 phone(字段名不一致)
        @Mapping(source = "phoneNum", target = "phone")
    })
    UserEntity dtoToEntity(UserDTO userDTO);
​
    /**
     * 实体 → DTO(反向映射,字段名对应规则与正向一致)
     * 可复用正向映射规则,用 @InheritInverseConfiguration 简化
     */
    @InheritInverseConfiguration(name = "dtoToEntity")
    @Mapping(
        source = "birth",
        target = "birth",
        dateFormat = "yyyy-MM-dd" // 反向转换也需指定日期格式
    )
    UserDTO entityToDto(UserEntity userEntity);
​
    /**
     * 自定义转换逻辑(如特殊格式处理、复杂计算)
     * 若 MapStruct 内置转换不满足,可定义默认方法(default)
     */
    default Integer stringToAge(String ageStr) {
        if (ageStr == null || ageStr.trim().isEmpty()) {
            return 0; // 空值默认 0
        }
        try {
            return Integer.parseInt(ageStr.trim());
        } catch (NumberFormatException e) {
            return 0; // 格式错误默认 0
        }
    }
​
    // 反向转换:Integer → String
    default String ageToString(Integer age) {
        return age == null ? "" : String.valueOf(age);
    }
}

步骤 4:编译项目,查看生成的实现类

MapStruct 会在 编译期 生成 UserConvertImpl 实现类(无需手动写),路径在 target/generated-sources/annotations/ 下,核心逻辑如下(自动生成的纯 Java 代码):

// 自动生成的实现类(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());
        // 调用自定义的 stringToAge 方法转换类型
        userEntity.setAge(stringToAge(userDTO.getAge()));
        // 自动按指定格式转换 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) {
        // 类似正向转换逻辑,自动处理字段映射和类型转换
    }
​
    // 自动生成自定义方法的调用逻辑...
}

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

通过 @Autowired 注入 UserConvert,直接调用映射方法:

@Service
public class UserService {
​
    @Autowired
    private UserConvert userConvert; // 注入 MapStruct 映射器
​
    public void addUser(UserDTO userDTO) {
        // 1. DTO → 实体(自动完成字段映射和类型转换)
        UserEntity userEntity = userConvert.dtoToEntity(userDTO);
        
        // 2. 存入数据库(假设用 MyBatis 操作)
        // userMapper.insert(userEntity);
        
        System.out.println("转换后的实体:" + userEntity);
    }
​
    public UserDTO getUserById(Long id) {
        // 1. 从数据库查询实体
        UserEntity userEntity = new UserEntity(id, "张三", 25, LocalDate.of(2000, 1, 1), "13800138000");
        
        // 2. 实体 → DTO(反向转换)
        UserDTO userDTO = userConvert.entityToDto(userEntity);
        
        return userDTO;
    }
}

测试效果:

// 测试 DTO → 实体
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)
​
// 测试实体 → 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 核心注解与高级特性

1. 核心注解(常用)

注解作用示例
@Mapper标识映射接口,指定组件模型(如 Spring)@Mapper(componentModel = "spring")
@Mapping单个字段映射规则@Mapping(source = "name", target = "userName")
@Mappings多个 @Mapping 的集合包裹多个 @Mapping 注解
@InheritInverseConfiguration继承反向映射规则(避免重复写注解)@InheritInverseConfiguration(name = "dtoToEntity")
@MappingTarget映射到已存在的对象(更新属性)void updateEntity(UserDTO dto, @MappingTarget UserEntity entity)
@Named命名自定义转换方法(用于复杂映射)@Named("stringToAge") default Integer stringToAge(String s) {}

2. 高级特性

(1)集合映射(自动支持 List/Set/Array)

MapStruct 会自动为集合生成映射方法,无需手动定义:

@Mapper(componentModel = "spring")
public interface UserConvert {
    // 单个对象映射(已定义)
    UserEntity dtoToEntity(UserDTO dto);
    UserDTO entityToDto(UserEntity entity);
​
    // 集合映射(自动生成,无需手动写实现)
    List<UserEntity> dtoListToEntityList(List<UserDTO> dtoList);
    Set<UserDTO> entitySetToDtoSet(Set<UserEntity> entitySet);
}

(2)自定义转换逻辑(复杂场景)

若内置转换不满足(如枚举转换、对象嵌套),可定义 default 方法或单独的转换类:

// 示例:枚举转换
public enum GenderEnum {
    MALE(1, "男"), FEMALE(2, "女");
    // getter/setter/静态方法...
}
​
// DTO 中 gender 是 String 类型("男"/"女"),实体中是 GenderEnum
@Mapping(
    source = "gender",
    target = "gender",
    qualifiedByName = "genderStrToEnum" // 指定自定义转换方法
)
UserEntity dtoToEntity(UserDTO dto);
​
// 自定义枚举转换方法(用 @Named 标识)
@Named("genderStrToEnum")
default GenderEnum genderStrToEnum(String genderStr) {
    if ("男".equals(genderStr)) return GenderEnum.MALE;
    if ("女".equals(genderStr)) return GenderEnum.FEMALE;
    return null;
}

(3)依赖注入其他服务

componentModel = "spring",映射器可注入 Spring Bean(如业务服务、工具类):

@Mapper(componentModel = "spring")
public interface UserConvert {
​
    @Autowired
    UserService userService; // 注入 Spring 服务
​
    @Mapping(
        source = "userId",
        target = "deptId",
        qualifiedByName = "getDeptIdByUserId" // 调用注入的服务
    )
    UserEntity dtoToEntity(UserDTO dto);
​
    @Named("getDeptIdByUserId")
    default Long getDeptIdByUserId(Long userId) {
        // 调用业务服务获取部门ID(复杂逻辑)
        return userService.getDeptIdByUserId(userId);
    }
}

(4)日期 / 数字格式转换

通过 dateFormat/numberFormat 指定格式:

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

五、注意事项

  1. 编译期生成代码

    • 必须引入 mapstruct-processor 依赖,否则不会生成实现类,运行时会报「找不到实现类」错误;
    • 若用 IDEA,需开启「Annotation Processing」(Settings → Build → Compiler → Annotation Processors),否则 IDEA 可能不识别生成的类。
  2. 与 Lombok 兼容

    • 确保 Lombok 版本 ≥ 1.18.16,MapStruct 版本 ≥ 1.4.2;
    • 若字段无 getter/setter(如用 @Data 生成),MapStruct 无法识别字段,需确保 Lombok 正确生成访问器。
  3. 字段名匹配规则

    • 默认按「字段名相同」映射(忽略大小写?不,严格匹配);
    • 若字段名是 userName(实体)和 user_name(DTO),可开启 unmappedTargetPolicy = ReportingPolicy.IGNORE 忽略未映射字段,或手动指定 @Mapping

总结

MapStruct 是 Java Bean 映射的最优解,核心优势:

  1. 类型安全:编译期检查字段名、类型是否匹配,避免运行时错误;
  2. 效率高:无反射,编译期生成原生 Java 代码,性能远超 BeanUtils
  3. 灵活:支持字段映射、类型转换、自定义逻辑、依赖注入;
  4. 简化代码:无需手动写 setter 拷贝,注解驱动开发。

到此这篇关于Java中MapStruct转换实体的步骤实现的文章就介绍到这了,更多相关Java MapStruct转换实体内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Cloud Gateway打造可扩展的微服务网关

    Spring Cloud Gateway打造可扩展的微服务网关

    微服务网关是一个位于客户端和后端微服务之间的服务器,用于处理所有与客户端的通信,Spring Cloud Gateway都是一个值得考虑的选择,它将帮助您更好地管理和保护您的微服务,感兴趣的朋友一起看看吧
    2023-11-11
  • Java实现猜数字小游戏代码

    Java实现猜数字小游戏代码

    大家好,本篇文章主要讲的是Java实现猜数字小游戏代码,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • java客户端连接ssh失败问题的两种解决方法

    java客户端连接ssh失败问题的两种解决方法

    有的运维工具使用了java的ssh客户端,这些客户端和服务端间有时会出现加密算法协商失败和主机密钥类型协商失败的问题,该问题是由于新客户端/服务端禁用了相关的不安全算法和密钥类型,本文简要记录下该问题的解决方法以备不时之需
    2026-01-01
  • 详解springboot如何更新json串里面的内容

    详解springboot如何更新json串里面的内容

    这篇文章主要为大家介绍了springboot 如何更新json串里面的内容,文中有详细的解决方案供大家参考,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-10-10
  • ClassLoader类加载源码解析

    ClassLoader类加载源码解析

    这篇文章主要为大家详细解析了ClassLoader类加载源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • Java的Spring框架中bean的继承与内部bean的注入

    Java的Spring框架中bean的继承与内部bean的注入

    这篇文章主要介绍了Java的Spring框架中bean的继承与内部bean的注入,Spring框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-12-12
  • 关于MyBatis中SqlSessionFactory和SqlSession简解

    关于MyBatis中SqlSessionFactory和SqlSession简解

    这篇文章主要介绍了MyBatis中SqlSessionFactory和SqlSession简解,具有很好的参考价值,希望大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 动态修改spring aop 切面信息提升自动日志输出框架效率

    动态修改spring aop 切面信息提升自动日志输出框架效率

    这篇文章主要为大家介绍了动态修改spring aop切面信息提升自动日志输出框架效率,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • 利用Java实现动态加载数据库

    利用Java实现动态加载数据库

    这篇文章主要为大家详细介绍了一个java小案例,即动态加载数据库信息,文中的示例代码简洁易懂,具有一定的学习价值,感兴趣的小伙伴可以了解一下
    2023-10-10
  • SpringBoot3整合 Elasticsearch 8.x 使用Repository构建增删改查示例应用

    SpringBoot3整合 Elasticsearch 8.x 使用Repository构

    我们构建了一个完整的 Spring Boot 3 和 Elasticsearch 8.x 的增删改查示例应用,使用 Spring Data Elasticsearch Repository,我们能够快速实现对 Elasticsearch 的基本 CRUD 操作,简化了开发流程,希望这个示例能够帮助你理解如何在项目中有效使用 Elasticsearch!
    2024-11-11

最新评论