在数据驱动的应用程序中工作时,Java对象映射是一项必要的任务,因为它允许我们在不同的数据结构之间进行转换,简化数据访问和操作。为了帮助Java开发人员处理映射挑战,多个Java映射框架已经在多年内被开发出来。每个框架都有其自身的优点和缺点,使得开发人员难以选择适合他们使用情况的正确框架。
在本文中,我们将深入探讨四个流行的Java映射框架:MapStruct、ModelMapper和JMapper。我们将探讨每个框架的使用,以及提供代码示例,帮助你了解如何在实践中使用它们。
通过本文,你将对这些Java映射框架有扎实的了解,并能够选择最适合你项目特定需求的框架。
🚩 起点
在Java应用程序中,使用不同的数据结构来处理不同的目的是很常见的。 例如,你可能有一个对象模型,代表你的域中的实体,例如客户、订单或产品。同时,你可能需要与不同的数据结构进行交互,例如数据传输对象(DTO),用于在应用程序的不同层之间或外部系统之间传输数据。
需要在不同的数据结构之间进行转换,例如域对象和DTO,这导致了Java映射框架的开发。这些框架自动化了将一个对象映射到另一个对象的过程,节省了开发人员的时间并降低了错误的可能性。
**DTO或数据传输对象是一个Java对象,用于封装数据并在不同的层或系统之间传输数据。**DTO通常包含域对象的一部分字段,并且它们被设计为轻量级和高效,以便在网络上或应用程序的不同部分之间传输。通过使用DTO,开发人员可以将域模型的关注点与数据传输的关注点分离,使他们的代码更模块化和更易于维护。
对于我们的案例,我们将定义域实体User:
public class User{
private Long id;
private String name;
private String surname;
private String email;
private LocalDate creationDate;
private List<String> addresses;
private String role;
// Getters y setters
}
以及相应的DTO:
public class UserDto {
private String name;
private String surname;
private String email;
private List<String> addresses;
// Getters y setters
}
这些将是我们将使用和测试的类和不同映射框架。
📕 MapStruct
要开始使用MapStruct,我们需要将maven依赖项添加到我们项目的pom.xml中:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
让我们还在maven-compiler-plugin
插件的配置部分中添加annotationProcessorPaths
部分。
在构建过程中,使用mapstruct-processor
生成映射器实现:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
一旦我们完成了所有这些,我们就可以定义我们的映射器。要做到这一点,我们只需要定义一个接口,我们将其称为UserMapper
,并使用@Mapper注释标记它。
(_🚩_如果我们想要将其注入为Bean
到任何其他Spring组件中,需要添加带有值componentModel =
的参数:"spring”
):
@Mapper(componentModel = "spring")
public interface UserMapper {
UserDto toDto(User user);
User toEntity(UserDto dto);
}
在这种情况下,由于DTO和实体的字段名称匹配,因此无需向我们的映射器添加任何其他内容。
但是如果一些字段不匹配 ,例如在DTO中名称以西班牙语“nombre”定义,则我们需要在映射方法上方添加注释@Mapping ,指示源字段和目标字段的名称,例如:
@Mapping(target = "nombre", source = "name")
UserDto toDto(User user);
现在我们已经准备好使用我们的映射器了,例如在我们的服务中。在我们的服务中,我们将使用 @Autowired 注释注入它,然后调用我们在接口中定义的相应方法,例如:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserMapper userMapper;
@Transactional(readOnly = true)
public UserDto getUser(Long id) {
return userMapper.toDto(userRepository.findById(id).orElseThrow(() -> new EntityNotFoundException("User not found")));
}
}
📘 ModelMapper
要开始使用ModelMapper,我们需要将maven依赖项添加到我们项目的pom.xml中:
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.4.5</version>
</dependency>
与前一个不同,ModelMapper不需要更多的配置方面,只需在Maven项目的_pom.xml_文件中定义依赖项,我们就拥有了使用这个工具所需的一切。
在我们的情况下,由于我们想将其注入为Bean
到我们的服务中,因此我们将不得不像这样在配置类中定义它:
@Configuration
public class Mapper {
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
完成后,我们可以使用**@Autowired注释注入它。要使用它,我们将使用ModelMapper**提供的map
方法,作为第一个参数传递要转换的对象,作为第二个参数传递要将对象转换为的类。因此,这将是其使用示例:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private ModelMapper mapper;
@Transactional(readOnly = true)
public UserDto getUser(Long id) {
return mapper.map(userRepository.findById(id).orElseThrow(() -> new EntityNotFoundException("User doesnt exist")), UserDto.class);
}
}
例如,如果我们想要自定义字段映射,例如上面提到的“name”字段的示例,则需要获取负责映射两个对象之间的typeMap,并将此规则添加到我们定义的映射器中:
mapper.getTypeMap(User.class,UserDto.class).addMapping(User::getName,UserDto::setNombre);
📗 JMapper
要开始使用JMapper,我们需要将maven依赖项添加到我们项目的_pom.xml_中:
<dependency>
<groupId>com.googlecode.jmapper-framework</groupId>
<artifactId>jmapper-core</artifactId>
<version>1.6.0.1</version>
</dependency>
我们将为对象映射定义一个Bean
,与前一个案例相同,因此我们可以将其注入到我们的服务中:
@Configuration
public class Mapper {
@Bean
public JMapper<UserDto,User> userMapper() {
return new JMapper<>(UserDto.class, User.class);
}
}
为了能够执行映射,JMapper需要添加一个额外的方面,即源和目标对象的字段对应关系。对于此,JMapper有3种不同的方法:使用JMapperAPI,使用注释或使用XML配置。对于我们的示例,我们将使用注释,但你可以在以下链接中找到更多信息:(https://www.baeldung.com/jmapper)。只需要在 DTO 类的每个属性上添加 @JMap 注释即可完成操作:
@Data
public class UserDto implements Serializable {
@JMap
private String name;
@JMap
private String surname;
@JMap
private String email;
@JMap
private List<String> addresses;
}
一旦我们拥有了所需的一切,我们只需要将 Bean
注入到服务中,并调用 JMapper 提供的相应方法,即可从 User
实体中获取 UserDto
对象:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private JMapper<UserDto, User> userMapper;
@Transactional(readOnly = true)
public UserDto getUser(Long id) {
return userMapper.getDestination(userRepository.findById(id).orElseThrow(() -> new EntityNotFoundException("User doesnt exist")));
}
}
要创建自定义属性映射,只需在目标类的 @JMap 注释中指定源属性的名称即可。例如,如果 UserDto
有一个与实体 User
的 name
字段对应的 nombre
字段:
@JMap("name")
private String nombre;
📙 在结束文章之前,我想特别提到其他存在的框架,比如:
- Orika (https://www.baeldung.com/orika-mapping)
- Dozer (https://www.baeldung.com/dozer).
🚨 此外,我想向你展示以下链接:https://www.baeldung.com/java-performance-mapping-frameworks,其中对不同情况下的性能和效率进行了深入分析。
🏁结论
总之,在使用 Java 映射框架时,需要考虑易用性、性能和自定义选项等因素。选择正确的框架取决于项目的具体要求和偏好。通过利用这些框架,你可以简化对象映射并增强 Java 应用程序的效率。
每个框架的使用取决于项目的需求或每个用户发现的易用性。在本文中,我试图讨论主要框架以及如何在我们的项目中实现它们。
评论(0)