SpringBoot實(shí)現(xiàn)數(shù)據(jù)轉(zhuǎn)換的四種對(duì)象映射方案
方案一:手動(dòng)映射
手動(dòng)映射是最基礎(chǔ)也是最直接的方法,通過(guò)顯式編寫(xiě)代碼將一個(gè)對(duì)象的屬性復(fù)制到另一個(gè)對(duì)象。
實(shí)現(xiàn)方式
首先定義兩個(gè)示例類(lèi):
// 用戶實(shí)體類(lèi) @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類(lèi) public class UserDTO { private Long id; private String username; private String email; private String phone; // 注意命名差異 private LocalDateTime registerTime; // 映射自createTime // getter和setter省略 }
手動(dòng)映射實(shí)現(xiàn):
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()); // 不同名稱(chēng)字段映射 dto.setRegisterTime(entity.getCreateTime()); // 語(yǔ)義轉(zhuǎn)換 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()); // 不同名稱(chēng)字段映射 entity.setCreateTime(dto.getRegisterTime()); // 語(yǔ)義轉(zhuǎn)換 return entity; } // 集合轉(zhuǎn)換方法 public static List<UserDTO> toDTOList(List<UserEntity> entities) { if (entities == null) { return Collections.emptyList(); } return entities.stream() .map(UserMapper::toDTO) .collect(Collectors.toList()); } }
優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 完全掌控映射邏輯,靈活性最高
- 不依賴(lài)第三方庫(kù),無(wú)學(xué)習(xí)成本
- 映射邏輯清晰直觀,易于調(diào)試
- 性能最優(yōu),無(wú)反射開(kāi)銷(xiāo)
缺點(diǎn):
- 代碼冗長(zhǎng),大量重復(fù)勞動(dòng)
- 屬性變更需要手動(dòng)同步修改轉(zhuǎn)換代碼
- 隨著對(duì)象數(shù)量增加,維護(hù)成本劇增
- 容易出現(xiàn)人為錯(cuò)誤,如遺漏字段
方案二:MapStruct
MapStruct是一個(gè)代碼生成器,它基于約定優(yōu)于配置的方法,在編譯時(shí)生成類(lèi)型安全的對(duì)象映射代碼。
實(shí)現(xiàn)方式
1. 添加依賴(lài)
<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); // 默認(rèn)值處理 @Mapping(target = "email", defaultValue = "no-email@example.com") UserDTO toDTOWithDefaultEmail(UserEntity entity); // 自定義方法處理復(fù)雜轉(zhuǎn)換 default String formatPhoneNumber(String phoneNumber) { if (phoneNumber == null || phoneNumber.trim().isEmpty()) { return null; } // 格式化電話號(hào)碼邏輯 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); } }
優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 編譯時(shí)生成代碼,運(yùn)行時(shí)性能極高
- 編譯時(shí)類(lèi)型安全檢查,錯(cuò)誤提前發(fā)現(xiàn)
- 代碼簡(jiǎn)潔,開(kāi)發(fā)效率高
- 支持復(fù)雜映射和自定義轉(zhuǎn)換邏輯
- 簡(jiǎn)化集合映射,無(wú)需手動(dòng)循環(huán)
缺點(diǎn):
- 需要額外依賴(lài)和配置
- 調(diào)試相對(duì)復(fù)雜(需查看生成的代碼)
方案三:ModelMapper
ModelMapper是一個(gè)靈活、強(qiáng)大的對(duì)象映射庫(kù),通過(guò)反射機(jī)制實(shí)現(xiàn)自動(dòng)映射。
實(shí)現(xiàn)方式
1. 添加依賴(lài)
<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); } }
優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 使用簡(jiǎn)單,API友好
- 配置靈活,支持多種映射策略
- 運(yùn)行時(shí)動(dòng)態(tài)映射,無(wú)需預(yù)定義
- 支持深層映射和復(fù)雜對(duì)象圖
- 類(lèi)型轉(zhuǎn)換內(nèi)置支持
缺點(diǎn):
- 基于反射,性能要求極高的場(chǎng)景可能不適合
- 運(yùn)行時(shí)類(lèi)型不安全,可能產(chǎn)生運(yùn)行時(shí)異常
- 映射邏輯不直觀,調(diào)試?yán)щy
- 復(fù)雜映射需要額外配置
方案四:Spring BeanUtils和BeanCopier
Spring框架提供了多種Bean屬性復(fù)制工具,其中BeanUtils是基于反射的,而B(niǎo)eanCopier是基于字節(jié)碼生成的高性能方案。
實(shí)現(xiàn)方式
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(); // 復(fù)制相同名稱(chēng)的屬性 BeanUtils.copyProperties(entity, dto); // 手動(dòng)處理不同名稱(chēng)的屬性 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); // 手動(dòng)處理不同名稱(chēng)的屬性 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 { // 創(chuàng)建并緩存BeanCopier實(shí)例,提高性能 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(); // 復(fù)制相同名稱(chēng)的屬性 ENTITY_TO_DTO.copy(entity, dto, null); // 手動(dòng)處理不同名稱(chēng)的屬性 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); // 手動(dòng)處理不同名稱(chēng)的屬性 entity.setPhoneNumber(dto.getPhone()); entity.setCreateTime(dto.getRegisterTime()); return entity; } }
優(yōu)缺點(diǎn)分析
Spring BeanUtils
優(yōu)點(diǎn):
- 開(kāi)箱即用,無(wú)需額外依賴(lài)
- 使用簡(jiǎn)單,API簡(jiǎn)潔
- 內(nèi)置在Spring框架中
- 支持屬性忽略功能
缺點(diǎn):
- 基于反射,性能要求極高的場(chǎng)景可能不適合
- 僅支持相同名稱(chēng)的屬性自動(dòng)復(fù)制
- 不同名稱(chēng)屬性需手動(dòng)處理
- 不支持深層嵌套對(duì)象自動(dòng)映射
CGLib BeanCopier
優(yōu)點(diǎn):
- 性能極高,接近手動(dòng)映射
- 基于字節(jié)碼生成,避免反射開(kāi)銷(xiāo)
- 緩存BeanCopier實(shí)例可進(jìn)一步提升性能
- Spring Boot默認(rèn)包含CGLib依賴(lài)
缺點(diǎn):
- 只能復(fù)制名稱(chēng)完全相同的屬性
- 不支持類(lèi)型轉(zhuǎn)換
- 配置選項(xiàng)有限
- 不同名稱(chēng)屬性仍需手動(dòng)處理
- 文檔較少,使用門(mén)檻略高
總結(jié)
對(duì)象映射是Spring Boot應(yīng)用中的常見(jiàn)需求,選擇合適的映射方案能顯著提高開(kāi)發(fā)效率和應(yīng)用性能。
根據(jù)項(xiàng)目規(guī)模、性能要求和團(tuán)隊(duì)熟悉度選擇合適的方案,同時(shí)注意映射過(guò)程中的深淺拷貝、循環(huán)引用等問(wèn)題。
合理使用對(duì)象映射工具,可以大幅減少樣板代碼,提高代碼質(zhì)量和可維護(hù)性。
以上就是SpringBoot數(shù)據(jù)轉(zhuǎn)換的4種對(duì)象映射方案的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot對(duì)象映射的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringMVC使用MultipartResolver實(shí)現(xiàn)文件上傳
MultipartResolver 用于處理文件上傳,當(dāng)收到請(qǐng)求時(shí) DispatcherServlet 的 checkMultipart() 方法會(huì)調(diào)用 MultipartResolver 的 isMultipart() 方法判斷請(qǐng)求中是否包含文件2023-02-02IDEA2022.2的簡(jiǎn)介、下載與安裝、配置教程
IDEA是JetBrains公司推出一個(gè)集成開(kāi)發(fā)工具,是Java開(kāi)發(fā)工具中的翹楚,基于這個(gè)開(kāi)發(fā)工具可以快速開(kāi)發(fā)我們的Java相關(guān)項(xiàng)目,本文重點(diǎn)給大家介紹IDEA2022.2的簡(jiǎn)介、下載與安裝、初步配置,感興趣的朋友一起看看吧2022-11-11springboot 接口返回字符串帶引號(hào)的問(wèn)題解決
本文主要介紹了springboot 接口返回字符串帶引號(hào)的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04解決java junit單元測(cè)試@Test報(bào)錯(cuò)的問(wèn)題
今天小編就為大家分享一篇解決java junit單元測(cè)試@Test報(bào)錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-11-11SpringCloud整合Netty集群實(shí)現(xiàn)WebSocket的示例代碼
文章主要介紹了SpringCloud整合Netty集群實(shí)現(xiàn)WebSocket的相關(guān)內(nèi)容,包括服務(wù)注冊(cè)和發(fā)現(xiàn)中心的配置,如使用Nacos、CommandLineRunner啟動(dòng)Netty服務(wù)等,還介紹了通過(guò)Redis實(shí)現(xiàn)消息發(fā)布訂閱的機(jī)制,需要的朋友可以參考下2024-11-11