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)其它相关文章!
精彩评论