实体类转换工具
本文档描述mapstract实体转换工具的引入
1 前言
mapstract相较于BeanUtils之类属性复制工具,效率高出很多,因为mapstrac采用的是java方法调用实现的属性复制,而不是反射。代码编译时,mapstrac会自动生成实现类的字节码文件(存在问题会编译不通过),反编译打开可看到实现类是通过get、set方法进行的属性赋值转换,经过测试,百万次转可以在几十毫秒内完成
2 功能引入
2.1 引入依赖
由于项目使用的是java8,引入如下依赖
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.3.1.Final</version>
</dependency>
由于mapstract在代码编译时会生成实现类,而实现类里主要是使用get\set方法进行属性的赋值,这和Lombok的功能存在冲突,所以要在编译插件引入如下代码
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<inherited>true</inherited>
<configuration>
<source>1.8</source>
<target>1.8</target>
<skip>true</skip>
<encoding>${project.build.sourceEncoding}</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
2.2 编写基础功能接口
基础功能接口具有四个基本功能接口,代码如下
public interface BaseConvert<SOURCE,TARGET> {
/** @description: 基础转换方法
当SOURCE的全部字段名和TARGET的一致时,把SOURCE类型转为TARGET类型,
当存在不一致的字段名时,请在子类里重写该方法,并使用{@link org.mapstruct.Mappings}注解
将不一致的字段建立映射关系,一点要注意source和target的顺序,例如
@Mappings({
@Mapping(source = "categoryCode",target = "code"),
@Mapping(source = "categoryName",target = "name")
})
*/
@InheritConfiguration
TARGET to(SOURCE source);
@InheritConfiguration
List<TARGET> to(Collection<SOURCE> sources);
/** 将TARGET类型的对象转为SOURCE类型 */
@InheritInverseConfiguration
SOURCE from(TARGET source);
@InheritInverseConfiguration
List<SOURCE> from(Collection<TARGET> sources);
}
其中@InheritConfiguration注解写在正向的
2.3 编写实体类转换接口
实体类转换接口继承基础功能接口,指定SOURCE和TARGET的顺序,每两个实体之间需要一个转换类, 例如如下的转换接口,不一致的字段映射写在{@link org.mapstruct.Mapping}注解里,可以写多个,也可以整体用{@link org.mapstruct.Mappings}注解包起来,注意source和target的顺序
@Mapper(componentModel = "spring")
public interface UserDTOWithVoConvert extends BaseConvert<UserDTO, UserVO> {
@Override
@Mapping(source = "categoryCode",target = "code")
@Mapping(source = "name.firstName",target = "firstName")
@Mapping(source = "name.lastName",target = "lastName")
// @Mapping(target = "name",expression = "java(userDTO.getName().getFirstName() + \".\" + userDTO.getName().getLastName())")
@Mapping(target = "name",expression = "java(userDTO.getFullName())")
@Mapping(target = "description",expression = "java(org.springframework.util.StringUtils.trimAllWhitespace(userDTO.getDescription()))")
@Mapping(target = "cnBirthday",expression = "java(userDTO.getCNBirthday())")
@Mapping(source = "birthday",target = "birthday",dateFormat = "yyyy-MM-dd")
@Mapping(source = "adulted",target = "adulted")
@Mapping(source = "healthy",target = "healthy",nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
@InheritConfiguration
UserVO to(UserDTO userDTO);
@Override
@Mapping(target = "name",expression = "java(source.getNameObject())")
@InheritInverseConfiguration
UserDTO from(UserVO source);
default Integer boolenToInteger(Boolean bool){
if(bool == null){
return null;
}
if(bool){
return 1;
}
return 0;
}
default Boolean boolenToInteger(Integer number){
if(number == null){
return null;
}
if(number==1){
return true;
}
return false;
}
}
2.4 工具类
public class ConvertUtil{
public static <SOURCE,TARGET> TARGET convertTo(SOURCE source,Class<? extends BaseConvert<SOURCE,TARGET>> convertClass){
BaseConvert<SOURCE, TARGET> convert = getConvert(convertClass);
return convert.to(source);
}
public static <SOURCE,TARGET> List<TARGET> convertTo(Collection<SOURCE> sources, Class<? extends BaseConvert<SOURCE,TARGET>> convertClass){
if ( sources == null ) {
return null;
}
BaseConvert<SOURCE, TARGET> convert = getConvert(convertClass);
return convert.to(sources);
}
public static <SOURCE,TARGET> SOURCE convertFrom(TARGET source,Class<? extends BaseConvert<SOURCE,TARGET>> convertClass){
if ( sources == null ) {
return null;
}
BaseConvert<SOURCE, TARGET> convert = getConvert(convertClass);
return convert.from(source);
}
public static <SOURCE,TARGET> List<SOURCE> convertFrom(Collection<TARGET> sources, Class<? extends BaseConvert<SOURCE,TARGET>> convertClass){
if ( sources == null ) {
return null;
}
BaseConvert<SOURCE, TARGET> convert = getConvert(convertClass);
return convert.from(sources);
}
//从容器中获取转换接口实现类
public static <SOURCE,TARGET> BaseConvert<SOURCE,TARGET> getConvert(Class<? extends BaseConvert<SOURCE,TARGET>> convertClass){
return SpringContextUtil.getBean(convertClass);
}
}
//SpringContextUtil里保存了spring容器的上下文
3 使用方法
直接使用ConvertUtil的方法进行调用即可,例如
UserVO userVO = ConvertUtil.convertTo(userDTO,UserDTOWithVoConvert.class);
userDTO = ConvertUtil.convertFrom(userVO,UserDTOWithVoConvert.class);
List<UserVO> userVOList = ConvertUtil.convertTo(userDTOList,UserDTOWithVoConvert.class);
List<UserDTO> dtoList = ConvertUtil.convertFrom(userVOList,UserDTOWithVoConvert.class);
4 进阶功能
更多功能可参考官方文档进行使用 https://mapstruct.org/documentation/1.3/reference/html/
5 注意事项
除了和Lombok搭配使用需要在编译插件添加对应的编译依赖外,集成swagger2时,要将其中的mapstract依赖排除掉
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
<exclusions>
<exclusion>
<artifactId>mapstruct</artifactId>
<groupId>org.mapstruct</groupId>
</exclusion>
</exclusions>
</dependency>