SpringBoot實(shí)現(xiàn)數(shù)據(jù)轉(zhuǎn)換的四種對(duì)象映射方案
方案一:手動(dòng)映射
手動(dòng)映射是最基礎(chǔ)也是最直接的方法,通過(guò)顯式編寫代碼將一個(gè)對(duì)象的屬性復(fù)制到另一個(gè)對(duì)象。
實(shí)現(xiàn)方式
首先定義兩個(gè)示例類:
// 用戶實(shí)體類
@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省略
}
手動(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()); // 不同名稱字段映射
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()); // 不同名稱字段映射
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):
- 完全掌控映射邏輯,靈活性最高
- 不依賴第三方庫(kù),無(wú)學(xué)習(xí)成本
- 映射邏輯清晰直觀,易于調(diào)試
- 性能最優(yōu),無(wú)反射開銷
缺點(diǎn):
- 代碼冗長(zhǎng),大量重復(fù)勞動(dòng)
- 屬性變更需要手動(dòng)同步修改轉(zhuǎn)換代碼
- 隨著對(duì)象數(shù)量增加,維護(hù)成本劇增
- 容易出現(xiàn)人為錯(cuò)誤,如遺漏字段
方案二:MapStruct
MapStruct是一個(gè)代碼生成器,它基于約定優(yōu)于配置的方法,在編譯時(shí)生成類型安全的對(duì)象映射代碼。
實(shí)現(xiàn)方式
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);
// 默認(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í)類型安全檢查,錯(cuò)誤提前發(fā)現(xiàn)
- 代碼簡(jiǎn)潔,開發(fā)效率高
- 支持復(fù)雜映射和自定義轉(zhuǎn)換邏輯
- 簡(jiǎn)化集合映射,無(wú)需手動(dòng)循環(huán)
缺點(diǎn):
- 需要額外依賴和配置
- 調(diào)試相對(duì)復(fù)雜(需查看生成的代碼)
方案三:ModelMapper
ModelMapper是一個(gè)靈活、強(qiáng)大的對(duì)象映射庫(kù),通過(guò)反射機(jī)制實(shí)現(xiàn)自動(dòng)映射。
實(shí)現(xiàn)方式
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);
}
}
優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 使用簡(jiǎn)單,API友好
- 配置靈活,支持多種映射策略
- 運(yùn)行時(shí)動(dòng)態(tài)映射,無(wú)需預(yù)定義
- 支持深層映射和復(fù)雜對(duì)象圖
- 類型轉(zhuǎn)換內(nèi)置支持
缺點(diǎn):
- 基于反射,性能要求極高的場(chǎng)景可能不適合
- 運(yùn)行時(shí)類型不安全,可能產(chǎn)生運(yùn)行時(shí)異常
- 映射邏輯不直觀,調(diào)試?yán)щy
- 復(fù)雜映射需要額外配置
方案四:Spring BeanUtils和BeanCopier
Spring框架提供了多種Bean屬性復(fù)制工具,其中BeanUtils是基于反射的,而BeanCopier是基于字節(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ù)制相同名稱的屬性
BeanUtils.copyProperties(entity, dto);
// 手動(dò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)處理不同名稱的屬性
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ù)制相同名稱的屬性
ENTITY_TO_DTO.copy(entity, dto, null);
// 手動(dò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)處理不同名稱的屬性
entity.setPhoneNumber(dto.getPhone());
entity.setCreateTime(dto.getRegisterTime());
return entity;
}
}
優(yōu)缺點(diǎn)分析
Spring BeanUtils
優(yōu)點(diǎn):
- 開箱即用,無(wú)需額外依賴
- 使用簡(jiǎn)單,API簡(jiǎn)潔
- 內(nèi)置在Spring框架中
- 支持屬性忽略功能
缺點(diǎn):
- 基于反射,性能要求極高的場(chǎng)景可能不適合
- 僅支持相同名稱的屬性自動(dòng)復(fù)制
- 不同名稱屬性需手動(dòng)處理
- 不支持深層嵌套對(duì)象自動(dòng)映射
CGLib BeanCopier
優(yōu)點(diǎn):
- 性能極高,接近手動(dòng)映射
- 基于字節(jié)碼生成,避免反射開銷
- 緩存BeanCopier實(shí)例可進(jìn)一步提升性能
- Spring Boot默認(rèn)包含CGLib依賴
缺點(diǎn):
- 只能復(fù)制名稱完全相同的屬性
- 不支持類型轉(zhuǎn)換
- 配置選項(xiàng)有限
- 不同名稱屬性仍需手動(dòng)處理
- 文檔較少,使用門檻略高
總結(jié)
對(duì)象映射是Spring Boot應(yīng)用中的常見需求,選擇合適的映射方案能顯著提高開發(fā)效率和應(yīng)用性能。
根據(jù)項(xiàng)目規(guī)模、性能要求和團(tuán)隊(duì)熟悉度選擇合適的方案,同時(shí)注意映射過(guò)程中的深淺拷貝、循環(huá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)文章
Springboot項(xiàng)目中kaptcha驗(yàn)證碼的使用方式
這篇文章主要介紹了Springboot項(xiàng)目中kaptcha驗(yàn)證碼的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(12)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07
java開發(fā)分布式服務(wù)框架Dubbo原理機(jī)制詳解
這篇文章主要為大家介紹了java開發(fā)分布式服務(wù)框架Dubbo的原理機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11
spring中Mapstruct屬性映射的實(shí)現(xiàn)
本文主要介紹了spring中Mapstruct屬性映射的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
替換jar包中的yml,class等文件的實(shí)現(xiàn)方式
文章介紹了如何在不回退版本的情況下,替換jar包中的特定文件來(lái)修復(fù)線上bug,具體步驟包括:準(zhǔn)備文件、下載jar包、查看文件路徑、解壓文件、替換文件、重新打包文件、驗(yàn)證替換、重新上傳jar包并測(cè)試2025-12-12
Java字符串格式化工具String.format()完整用法指南
String.format是Java中的一個(gè)靜態(tài)方法,用于創(chuàng)建格式化的字符串,這篇文章主要介紹了Java字符串格式化工具String.format()完整用法指南的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2026-01-01

