开发者

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

目录
  • 方案一:手动映射
    • 实现方式
    • 优缺点分析
  • 方案二:MapStruct
    • 实现方式
      • 1. 添加依赖
      • 2. 定义映射接口
      • 3. 使用方式
    • 优缺点分析
    • 方案三:ModelMapper
      • 实现方式
        • 1. 添加依赖
        • 2. 配置ModelMapper
        • 3. 使用方式
      • 优缺点分析
      • 方案四:Spring BeanUtils和BeanCopier
        • 实现方式
          • 1. Spring BeanUtils
          • 2. CGLib BeanCopier
        • 优缺点分析
        • 总结

          方案一:手动映射

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

          实现方式

          首先定义两个示例类:

          // 用户实体类
          @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> entit编程客栈ies) {
                  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 = userjavascriptRepository.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() {
                  ModelMapwww.devze.comper 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(UserEn编程客栈tity.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对象映射的资料请关注编程客栈(www.devze.com)其它相关文章!

          0

          上一篇:

          下一篇:

          精彩评论

          暂无评论...
          验证码 换一张
          取 消

          最新开发

          开发排行榜