一、简介
Mapstruct是一种实体类映射框架,能够通过Java注解将一个实体类的属性安全地赋值给另一个实体类。有了Mapstruct,只需要定义一个映射器接口,声明需要映射的方法,在编译过程中,Mapstruct会自动生成该接口的实现类,实现将源对象映射到目标对象的效果。
二、优点
1、安全性高,因为是编译期就实现源对象到目标对象的映射, 如果编译器能够通过,运行期就不会报错。
2、性能高,通过使⽤普通⽅法(getter/setter)调⽤⽽不是反射来快速执⾏。
3、如果有如下问题,编译时会抛出异常
- 映射不完整(并非所有目标属性都被映射)
- 映射不正确(找不到正确的映射方法或类型转换)
4、代码独立,没有运行时的依赖,更加规范
三、实践
<mapstruct.version>1.5.2.Final</mapstruct.version>
<!--包含mapstruct所需的注解-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<!--包含⽣成映射器实现的注释处理器-->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
3.1、基本映射
要创建映射器,只需使⽤所需的映射⽅法定义Java接口,并使⽤@Mapper 注解对其进⾏注释: @Mapper注解的componentModel属性有五种赋值:
- MappingConstants.ComponentModel.DEFAULT:默认,不使用任何组建类型,可以通过Mappers.getMapper(Class) 方式获取实例对象
- MappingConstants.ComponentModel.SPRING:在实现类上注解 @Component,可通过 @Autowired 方式注入
- MappingConstants.ComponentModel.JSR330:实现类上添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取。
- MappingConstants.ComponentModel.CDI:不懂
- MappingConstants.ComponentModel.JAKARTA:不懂
public class UserDTO {
private Long id;
private String username;
private String password;
private int age;
private Boolean bald;
private List<String> roles;
}
public class UserVO {
private Long id;
private String username;
private String password;
private Integer age;
private Boolean bald;
private List<String> roles;
}
@Mapper
public interface UserMap {
//循环调用的场景下提高性能的关键,通过UserMap.INSTANCE获取实例
UserMap INSTANCE = Mappers.getMapper(UserMap.class);
UserVO userDTOToVO(UserDTO userDTO);
}
3.2 指定方式映射
使用@Mapping注解可以指定映射目标字段的源字段、日期格式化、数字格式化、默认值或常量、表达式、忽略字段、空字段检查和映射策略、指定映射方法(不使用getter/setter)等,满足特定的映射需求。@Mapping注解的target属性必填。
//指定源字段,目标对象和源对象的属性名不一致
//指定源字段
@Mapping(target = "pwd", source = "password")
UserVO userDTOToVO(UserDTO userDTO);
当需要对多个字段进行操作时,可以使用@Mappings,也可以在方法上叠加@Mapping注解
@Mappings({
//指定源字段
@Mapping(target = "pwd", source = "password"),
//日期格式化,如果属性从字符串映射到日期,则该格式字符串可由SimpleDateFormat处理,反之亦然,当映射枚举常量时,将忽略所有其他属性类型
@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd"),
//数字格式化
@Mapping(target = "age", numberFormat = "$#.0"),
//指定默认值或常量,两者的区别是常量无论源对象是否有值,都会被覆盖
@Mapping(target = "bald", defaultValue = "true"),
//表达式,这个属性不能与source、defaultValue、defaultExpression、qualifiedBy、qualifiedByName或constant一起使用。
//需要包含完整的包名,否则可能找不到类
@Mapping(target = "username", expression = "java(org.apache.commons.lang3.StringUtils.substring(userDTO.getUsername(),0,2))"),
//忽略字段
@Mapping(target = "id", ignore = true)
})
UserVO userDTOToVO(UserDTO userDTO);
指定映射时调用的方法
//方式一:
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ToLocalDateTime {
}
public class LocalDateUtil{
/**
* 将Date类型的时间转换为LocalDateTime类型
* @param date
* @return
*/
@ToLocalDateTime
public static LocalDateTime convert(Date date){
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
}
//需要用uses属性导入方法所在的类
@Mapper(uses = LocalDateUtil.class)
public interface UserMap {
@Mapping(target = "createTime", qualifiedBy = ToLocalDateTime.class)
UserVO userDTOToVO(UserDTO userDTO);
}
--------------------------------------------------------------------------------------------------------
//方式二:
public class LocalDateUtil{
/**
* 将Date类型的时间转换为LocalDateTime类型
* @param date
* @return
*/
@Named("convertToLocalDateTime")
public static LocalDateTime convert(Date date){
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
}
@Mapper(uses = LocalDateUtil.class)
public interface UserMap {
@Mapping(target = "createTime", qualifiedByName = "convertToLocalDateTime")
UserVO userDTOToVO(UserDTO userDTO);
}
3.3更新bean对象
并不想要返回一个新的对象,而是想要更新传入对象的一些属性值时,将对象缺失的信息进行补充,这时可以使用@MappingTarget注解注释
/**
* 传入两个对象,userDTO会被userVO的值覆盖
*
* @param userVO
* @param userDTO
*/
void updateUserDTO(UserVO userVO, @MappingTarget UserDTO userDTO);
3.4多对一映射
我们在实际的业务中少不了将多个对象转换成一个的场景。MapStruct 当然也支持多转一的操作。
/**
* 源对象属性指定具体的字段
* @param userDTO
* @param processDef
* @return
*/
@Mapping(target = "id",source = "processDef.id")
UserVO convertUser(UserDTO userDTO, ProcessDef processDef);
3.5逆映射
在双向映射的情况下,例如从实体到DTO以及从DTO到实体,前向方法和反向方法的映射规则通常是相似的,并且可以通过切换source和来简单地反转target。使用注释@InheritInverseConfiguration表示方法应继承相应反向方法的反向配置
@Mapper(uses = LocalDateUtil.class)
public interface UserMap {
@Mapping(target = "createTime", qualifiedByName = "convertToLocalDateTime")
UserVO userDTOToVO(UserDTO userDTO);
@InheritConfiguration
UserDTO userVOToDTO(UserVO userVO);
}
3.6map映射
当需要将一个map对象映射到另一个map对象时可以使用@MapMapping注解
public interface SourceTargetMapper {
@MapMapping(valueDateFormat = "dd.MM.yyyy")
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
}
@MapMapping注解针对key和value提供了很多的属性,可以参照@Mapping注解,会按照配置分别对key和value进行映射。
package org.mapstruct;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.mapstruct.control.MappingControl;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface MapMapping {
String keyDateFormat() default "";
String valueDateFormat() default "";
String keyNumberFormat() default "";
String valueNumberFormat() default "";
Class<? extends Annotation>[] keyQualifiedBy() default {};
String[] keyQualifiedByName() default {};
Class<? extends Annotation>[] valueQualifiedBy() default {};
String[] valueQualifiedByName() default {};
Class<?> keyTargetType() default void.class;
Class<?> valueTargetType() default void.class;
NullValueMappingStrategy nullValueMappingStrategy() default NullValueMappingStrategy.RETURN_NULL;
Class<? extends Annotation> keyMappingControl() default MappingControl.class;
Class<? extends Annotation> valueMappingControl() default MappingControl.class;
}
以上基本能满足日常开发的需要,如有更多的需求或学习兴趣,可参阅官方文档:mapstruct.org/documentati…