SpringBoot实现数据转换的四种对象映射方案

 更新时间:2025年06月03日 08:37:48   作者:风象南  
在项目开发中,对象之间的相互转换是一个高频操作,尤其在分层架构的系统中,数据在实体对象(Entity)、数据传输对象(DTO)、值对象(VO)之间的转换尤为常见,选择一个高效、可靠的对象映射方案对提高系统性能和开发效率至关重要,本文将介绍4种对象映射方案

方案一:手动映射

手动映射是最基础也是最直接的方法,通过显式编写代码将一个对象的属性复制到另一个对象。

实现方式

首先定义两个示例类:

// 用户实体类
@Entity
@Table(name = "users")
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    private String email;
    private String phoneNumber;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    // getter和setter省略
}

// 用户DTO类
public class UserDTO {
    private Long id;
    private String username;
    private String email;
    private String phone; // 注意命名差异
    private LocalDateTime registerTime; // 映射自createTime
    // getter和setter省略
}

手动映射实现:

public class UserMapper {
    
    public static UserDTO toDTO(UserEntity entity) {
        if (entity == null) {
            return null;
        }
        
        UserDTO dto = new UserDTO();
        dto.setId(entity.getId());
        dto.setUsername(entity.getUsername());
        dto.setEmail(entity.getEmail());
        dto.setPhone(entity.getPhoneNumber()); // 不同名称字段映射
        dto.setRegisterTime(entity.getCreateTime()); // 语义转换
        
        return dto;
    }
    
    public static UserEntity toEntity(UserDTO dto) {
        if (dto == null) {
            return null;
        }
        
        UserEntity entity = new UserEntity();
        entity.setId(dto.getId());
        entity.setUsername(dto.getUsername());
        entity.setEmail(dto.getEmail());
        entity.setPhoneNumber(dto.getPhone()); // 不同名称字段映射
        entity.setCreateTime(dto.getRegisterTime()); // 语义转换
        
        return entity;
    }
    
    // 集合转换方法
    public static List<UserDTO> toDTOList(List<UserEntity> entities) {
        if (entities == null) {
            return Collections.emptyList();
        }
        
        return entities.stream()
                .map(UserMapper::toDTO)
                .collect(Collectors.toList());
    }
}

优缺点分析

优点:

  • 完全掌控映射逻辑,灵活性最高
  • 不依赖第三方库,无学习成本
  • 映射逻辑清晰直观,易于调试
  • 性能最优,无反射开销

缺点:

  • 代码冗长,大量重复劳动
  • 属性变更需要手动同步修改转换代码
  • 随着对象数量增加,维护成本剧增
  • 容易出现人为错误,如遗漏字段

方案二:MapStruct

MapStruct是一个代码生成器,它基于约定优于配置的方法,在编译时生成类型安全的对象映射代码。

实现方式

1. 添加依赖

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.3.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.3.Final</version>
    <scope>provided</scope>
</dependency>

2. 定义映射接口

@Mapper(componentModel = "spring")
public interface UserMapStruct {
    
    @Mapping(source = "phoneNumber", target = "phone")
    @Mapping(source = "createTime", target = "registerTime")
    UserDTO toDTO(UserEntity entity);
    
    @Mapping(source = "phone", target = "phoneNumber")
    @Mapping(source = "registerTime", target = "createTime")
    UserEntity toEntity(UserDTO dto);
    
    List<UserDTO> toDTOList(List<UserEntity> entities);
    
    // 默认值处理
    @Mapping(target = "email", defaultValue = "no-email@example.com")
    UserDTO toDTOWithDefaultEmail(UserEntity entity);
    
    // 自定义方法处理复杂转换
    default String formatPhoneNumber(String phoneNumber) {
        if (phoneNumber == null || phoneNumber.trim().isEmpty()) {
            return null;
        }
        // 格式化电话号码逻辑
        return phoneNumber.replaceAll("(\d{3})(\d{4})(\d{4})", "$1-$2-$3");
    }
}

3. 使用方式

@Service
public class UserService {
    
    private final UserMapStruct userMapper;
    
    @Autowired
    public UserService(UserMapStruct userMapper) {
        this.userMapper = userMapper;
    }
    
    public UserDTO getUserDTO(Long id) {
        UserEntity entity = userRepository.findById(id).orElseThrow();
        return userMapper.toDTO(entity);
    }
    
    public List<UserDTO> getAllUserDTOs() {
        List<UserEntity> entities = userRepository.findAll();
        return userMapper.toDTOList(entities);
    }
}

优缺点分析

优点:

  • 编译时生成代码,运行时性能极高
  • 编译时类型安全检查,错误提前发现
  • 代码简洁,开发效率高
  • 支持复杂映射和自定义转换逻辑
  • 简化集合映射,无需手动循环

缺点:

  • 需要额外依赖和配置
  • 调试相对复杂(需查看生成的代码)

方案三:ModelMapper

ModelMapper是一个灵活、强大的对象映射库,通过反射机制实现自动映射。

实现方式

1. 添加依赖

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>3.1.1</version>
</dependency>

2. 配置ModelMapper

@Configuration
public class ModelMapperConfig {
    
    @Bean
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();
        
        // 配置映射策略
        modelMapper.getConfiguration()
                .setMatchingStrategy(MatchingStrategies.STRICT)
                .setPropertyCondition(Conditions.isNotNull())
                .setFieldAccessLevel(Configuration.AccessLevel.PRIVATE)
                .setFieldMatchingEnabled(true);
        
        // 自定义字段映射
        PropertyMap<UserEntity, UserDTO> userMap = new PropertyMap<UserEntity, UserDTO>() {
            @Override
            protected void configure() {
                map().setPhone(source.getPhoneNumber());
                map().setRegisterTime(source.getCreateTime());
            }
        };
        
        modelMapper.addMappings(userMap);
        
        return modelMapper;
    }
}

3. 使用方式

@Service
public class UserService {
    
    private final ModelMapper modelMapper;
    private final UserRepository userRepository;
    
    @Autowired
    public UserService(ModelMapper modelMapper, UserRepository userRepository) {
        this.modelMapper = modelMapper;
        this.userRepository = userRepository;
    }
    
    public UserDTO getUserDTO(Long id) {
        UserEntity entity = userRepository.findById(id).orElseThrow();
        return modelMapper.map(entity, UserDTO.class);
    }
    
    public List<UserDTO> getAllUserDTOs() {
        List<UserEntity> entities = userRepository.findAll();
        return entities.stream()
                .map(entity -> modelMapper.map(entity, UserDTO.class))
                .collect(Collectors.toList());
    }
    
    public UserEntity createUser(UserDTO dto) {
        UserEntity entity = modelMapper.map(dto, UserEntity.class);
        return userRepository.save(entity);
    }
}

优缺点分析

优点:

  • 使用简单,API友好
  • 配置灵活,支持多种映射策略
  • 运行时动态映射,无需预定义
  • 支持深层映射和复杂对象图
  • 类型转换内置支持

缺点:

  • 基于反射,性能要求极高的场景可能不适合
  • 运行时类型不安全,可能产生运行时异常
  • 映射逻辑不直观,调试困难
  • 复杂映射需要额外配置

方案四:Spring BeanUtils和BeanCopier

Spring框架提供了多种Bean属性复制工具,其中BeanUtils是基于反射的,而BeanCopier是基于字节码生成的高性能方案。

实现方式

1. Spring BeanUtils

import org.springframework.beans.BeanUtils;

public class UserMapperWithBeanUtils {
    
    public static UserDTO toDTO(UserEntity entity) {
        if (entity == null) {
            return null;
        }
        
        UserDTO dto = new UserDTO();
        // 复制相同名称的属性
        BeanUtils.copyProperties(entity, dto);
        
        // 手动处理不同名称的属性
        dto.setPhone(entity.getPhoneNumber());
        dto.setRegisterTime(entity.getCreateTime());
        
        return dto;
    }
    
    public static UserEntity toEntity(UserDTO dto) {
        if (dto == null) {
            return null;
        }
        
        UserEntity entity = new UserEntity();
        BeanUtils.copyProperties(dto, entity);
        
        // 手动处理不同名称的属性
        entity.setPhoneNumber(dto.getPhone());
        entity.setCreateTime(dto.getRegisterTime());
        
        return entity;
    }
    
    public static List<UserDTO> toDTOList(List<UserEntity> entities) {
        if (entities == null) {
            return Collections.emptyList();
        }
        
        return entities.stream()
                .map(UserMapperWithBeanUtils::toDTO)
                .collect(Collectors.toList());
    }
}

2. CGLib BeanCopier

import org.springframework.cglib.beans.BeanCopier;

public class UserMapperWithBeanCopier {
    
    // 创建并缓存BeanCopier实例,提高性能
    private static final BeanCopier ENTITY_TO_DTO = BeanCopier.create(UserEntity.class, UserDTO.class, false);
    private static final BeanCopier DTO_TO_ENTITY = BeanCopier.create(UserDTO.class, UserEntity.class, false);
    
    public static UserDTO toDTO(UserEntity entity) {
        if (entity == null) {
            return null;
        }
        
        UserDTO dto = new UserDTO();
        // 复制相同名称的属性
        ENTITY_TO_DTO.copy(entity, dto, null);
        
        // 手动处理不同名称的属性
        dto.setPhone(entity.getPhoneNumber());
        dto.setRegisterTime(entity.getCreateTime());
        
        return dto;
    }
    
    public static UserEntity toEntity(UserDTO dto) {
        if (dto == null) {
            return null;
        }
        
        UserEntity entity = new UserEntity();
        DTO_TO_ENTITY.copy(dto, entity, null);
        
        // 手动处理不同名称的属性
        entity.setPhoneNumber(dto.getPhone());
        entity.setCreateTime(dto.getRegisterTime());
        
        return entity;
    }
}

优缺点分析

Spring BeanUtils

优点:

  • 开箱即用,无需额外依赖
  • 使用简单,API简洁
  • 内置在Spring框架中
  • 支持属性忽略功能

缺点:

  • 基于反射,性能要求极高的场景可能不适合
  • 仅支持相同名称的属性自动复制
  • 不同名称属性需手动处理
  • 不支持深层嵌套对象自动映射

CGLib BeanCopier

优点:

  • 性能极高,接近手动映射
  • 基于字节码生成,避免反射开销
  • 缓存BeanCopier实例可进一步提升性能
  • Spring Boot默认包含CGLib依赖

缺点:

  • 只能复制名称完全相同的属性
  • 不支持类型转换
  • 配置选项有限
  • 不同名称属性仍需手动处理
  • 文档较少,使用门槛略高

总结

对象映射是Spring Boot应用中的常见需求,选择合适的映射方案能显著提高开发效率和应用性能。

根据项目规模、性能要求和团队熟悉度选择合适的方案,同时注意映射过程中的深浅拷贝、循环引用等问题。

合理使用对象映射工具,可以大幅减少样板代码,提高代码质量和可维护性。

以上就是SpringBoot数据转换的4种对象映射方案的详细内容,更多关于SpringBoot对象映射的资料请关注脚本之家其它相关文章!

相关文章

  • Java项目日志脱敏解决方案

    Java项目日志脱敏解决方案

    为了保护客户的敏感信息,我们提出了四种日志脱敏处理方案,需要根据实际项目需求和情况进行选择,新项目或重构的项目,对于项目标准化要求较高的,推荐使用方案四,感兴趣的朋友参考下本文
    2024-10-10
  • Springboot项目中kaptcha验证码的使用方式

    Springboot项目中kaptcha验证码的使用方式

    这篇文章主要介绍了Springboot项目中kaptcha验证码的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • Java日常练习题,每天进步一点点(12)

    Java日常练习题,每天进步一点点(12)

    下面小编就为大家带来一篇Java基础的几道练习题(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望可以帮到你
    2021-07-07
  • java开发分布式服务框架Dubbo原理机制详解

    java开发分布式服务框架Dubbo原理机制详解

    这篇文章主要为大家介绍了java开发分布式服务框架Dubbo的原理机制详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-11-11
  • spring中Mapstruct属性映射的实现

    spring中Mapstruct属性映射的实现

    本文主要介绍了spring中Mapstruct属性映射的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • 替换jar包中的yml,class等文件的实现方式

    替换jar包中的yml,class等文件的实现方式

    文章介绍了如何在不回退版本的情况下,替换jar包中的特定文件来修复线上bug,具体步骤包括:准备文件、下载jar包、查看文件路径、解压文件、替换文件、重新打包文件、验证替换、重新上传jar包并测试
    2025-12-12
  • Spring框架 注解配置事务控制的流程

    Spring框架 注解配置事务控制的流程

    这篇文章主要介绍了Spring框架 注解配置事务控制的流程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • java语言图形用户登录界面代码

    java语言图形用户登录界面代码

    这篇文章主要为大家详细介绍了java语言图形用户登录界面代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Java LinkedList集合功能实例解析

    Java LinkedList集合功能实例解析

    这篇文章主要介绍了Java LinkedList集合功能实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Java字符串格式化工具String.format()完整用法指南

    Java字符串格式化工具String.format()完整用法指南

    String.format是Java中的一个静态方法,用于创建格式化的字符串,这篇文章主要介绍了Java字符串格式化工具String.format()完整用法指南的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2026-01-01

最新评论