ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

mapstruct常见用法梳理

2021-09-25 21:06:51  阅读:443  来源: 互联网

标签:UserDto 映射 mapstruct UserMapper private 梳理 用法 public


目录

一、前言

二、mapstruct使用前的准备

1、maven方式

2、Gradle方式

3、 Apache Ant方式

三、mapstruct使用方式梳理

1、最简单的映射

2、不同属性名映射

3、不同类型映射

        1)、日期格式转化        

        2)、数字格式转化

4、List映射 

5、Map和Set映射

6、枚举映射

四、总结

参考


一、前言

        在互联网企业中,随着业务越来越繁琐,导致系统架构越来越复杂,很多企业都使用DDD对系统进行拆分。DDD通常分很多层次,每个层次都有相应的逻辑,在不同层次之间进行调用时,难免需要对实体类进行转换,例如将UserDo转化成UserDto。目前业界有很多种对象映射工具,例如:

  1. json方式。将UserDo转化成json字符串后,再通过fastjson的JSON.parseObject方法将json字符串转化成UserDto类。
  2. BeanUtils.copyProperties()。BeanUtils.copyProperties()比较有名的有Apache的BeanUtils和Spring的BeanUtils。虽然两者性能上有所差异,但是都是通过反射实现的,总体性能还是欠佳。
  3. BeanCopier。BeanCopier通过字节码方式转换成性能最好的get和set方式,会动态生成一个被代理的类的子类,总体性能比BeanUtils好。
  4. 其他编译器转化工具。还有很多在编译期间进行转化的工具,例如MapStruct,Selma,Orika等。

        本文主要介绍mapstruct的常用方式。mapstruct是一个实现JSR269的bean映射工具。只需定义一个 mapper接口,该接口声明添加@Mapper注解,然后再mapper接口中定义一个转化方法。mapstruct就能在编译期间将一个实体类的属性值映射到另一个实体类中。本文只梳理mapstruct的一些常见用法,具体原理可以参考mapstruct原理解析

二、mapstruct使用前的准备

        mapstruct使用前需要在项目中引入对应的依赖。

1、maven方式

        如果是maven,则使用下列方式引入。

<properties>
    <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
</properties>
...
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
...
<build>
    <plugins>
        <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>${org.mapstruct.version}</version>
                    </path>
                    <!-- other annotation processors -->
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2、Gradle方式

        如果Gradle版本>= 4.6,需要添加下列代码到build.gradle中

dependencies {
    ...
    implementation 'org.mapstruct:mapstruct:1.4.2.Final'
 
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}

         如果Gradle版本 <4.6,需要引入下列代码到build.gradle中。

plugins {
    ...
    id 'net.ltgt.apt' version '0.21'
}
dependencies {
    ...
    compile 'org.mapstruct:mapstruct:1.4.2.Final'
 
    apt 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}

3、 Apache Ant方式

        如果使用Apache Ant,则需要在build.xml中引入下列代码。

...
<javac
    srcdir="src/main/java"
    destdir="target/classes"
    classpath="path/to/mapstruct-1.4.2.Final.jar">
    <compilerarg line="-processorpath path/to/mapstruct-processor-1.4.2.Final.jar"/>
    <compilerarg line="-s target/generated-sources"/>
</javac>
...

三、mapstruct使用方式梳理

        mapstruct使用方式比较多,还可以支持比较复杂的操作。下面依次列举常见的使用方式。

1、最简单的映射

        最简单的映射就是两个实体类的属性名和属性类型完全一一对应,这种方式最简单。为了直观起见,使用两个实体类进行说明。

public class UserDto {
    private int id;
    private String name;
}

public class UserDO {
    private int id;
    private String name;
}

        为了实现UserDto和UserDO之间实现映射,需要定义一个接口:

@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    UserDto toDto(UserDo userDo);
}

         上述UserMapper接口在编译期间会被mapstruct创建一个实现类UserMapperImpl,然后实现toDto方法。实现类如下代码所示:

public class UserMapperImpl implements UserMapper {
    @Override
    public UserDto toDto(UserDo userDo) {
        if (userDo == null) {
            return null;
        }

        UserDto userDto = new UserDto();
        if ( userDo.getId() != null ) {
            userDto.setId( userDo.getId() );
        }

        if ( userDo.getName() != null ) {
            userDto.setUserName( userDo.getName() );
        }
        return userDto    
}

2、不同属性名映射

        很多场景两个映射实体直接的属性名可能不同,但是却需要将他们的值进行映射,老规矩还是先列出两个映射实体,再介绍使用方式。

public class UserDto {
    private int id;
    private String name;
}


public class UserDo {
    private int id;
    private String userName;
}

         上述UserDto的name和UserDo的userName两个属性名不同。为了让mapstruct实现他们之间的映射,需要在方法上显示的标记。具体实现如下代码所示:

@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "userName", target = "name")
    UserDto toDto(UserDo userDo);
}

        @Mapping注解能够将UserDo中的userName对应到UserDto中的name字段。

        如果UserDo和UserDto有多个属性名不同,则需要使用@Mappings注解包裹@Mapping,假设UserDo和UserDto代码如下: 

public class UserDto {
    private int id;
    private String name;
    private int age;
}


public class UserDo {
    private int id;
    private String userName;
    private int userAge;
}

        需要在toDto方法上加上@Mappings注解。

@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mappings({
        @Mapping(source = "userName", target = "name"),
        @Mapping(source = "userAge", target = "age")
    })
    UserDto toDto(UserDo userDo);
}

3、不同类型映射

        1)、日期格式转化        

        mapstruct支持日期类型与其他类型的转化。如将LocalDate格式映射成Sting,可以使用如下方式:

public class UserDto {
    private int id;
    private String name;
    private int age;
    private String birthday;
}

public class UserDo {
    private int id;
    private String name;
    private int age;
    private LocalDate birthday;
}


@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "birthday", target = "birthday", dateFormat = "dd/MMM/yyyy")
    UserDto toDto(UserDo userDo);
}

        上述的@Mapping里添加了dateFormat,mapstruct使用下列代码将birthday转化成String类型。

DateTimeFormatter.ofPattern("dd/MMM/yyyy") .format(UserDo.getBirthday())

        2)、数字格式转化

        数字格式的转化,可以使用numberFormat进行转换。代码如下所示:

public class UserDto {
    private int id;
    private String name;
    private int age;
    private String wealth;
}

public class UserDo {
    private int id;
    private String name;
    private int age;
    private int wealth;
}


@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "wealth", target = "wealth", numberFormat = "$#.00")
    UserDto toDto(UserDo userDo);
}

4、List映射 

        mapstruct支持List的映射,使用List转化是需要有单个实体之间的转化方法,具体代码如下:

@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "wealth", target = "wealth", numberFormat = "$#.00")
    UserDto toDto(UserDo userDo);

    List<UserDto> toDtos(List<UserDo> userDoList);
}

            注意,假如UserDto中有个字段在UserDo中不存在,在生成代码时会报错。因此需要在toDto中添加@Mapping注解,将不存在的字段进行特殊映射或者直接忽略掉。代码如下所示:

public class UserDto {
    private int id;
    private String name;
    private int age;
    private String wealth;
}

public class UserDo {
    private int id;
    private String name;
    private int age;
}


@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(target = "wealth", ignore = true)
    UserDto toDto(UserDo userDo);

    List<UserDto> toDtos(List<UserDo> userDoList);
}

5、Map和Set映射

        Map和Set的映射与List类似,具体示例如下:

public class UserDto {
    private int id;
    private String name;
    private int age;
    private String wealth;
}

public class UserDo {
    private int id;
    private String name;
    private int age;
}


@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(target = "wealth", ignore = true)
    UserDto toDto(UserDo userDo);

    List<UserDto> toDtos(List<UserDo> userDoList);

    Set<UserDto> toDtoSet(Set<UserDo> userDoSet);

    Map<Long, UserDto> toDtoMap(Map<Long, UserDo> userDoMap);
}

6、枚举映射

       枚举映射与实体类的字段映射类似,mapstruct会根据相同的名称自动进行映射。不过如果字段名称不同,则需要使用@ValueMapping注解。代码示例如下:

public enum ExampleMappingEnum {
    ENUM1,
    ENUM2,
    ENUM_MAPPING3,
    ENUM_MAPPING4
}

public enum ExampleEnum {
    ENUM1,
    ENUM2,
    ENUM3
}

@Mapper
public interface ExampleEnumMapper {

    ExampleEnumMapper INSTANCE = Mappers.getMapper(ExampleEnumMapper.class);

    @ValueMappings({
            @ValueMapping(source = "ENUM_MAPPING3", target = "ENUM3"),
            @ValueMapping(source = "ENUM_MAPPING4", target = "ENUM3")
    })
    ExampleEnum map(ExampleMappingEnum exampleMappingEnum);

}

        上述代码的ExampleMappingEnum比ExampleEnum多一个值,并且枚举值也不同,需要通过@ValueMapping进行转化,mapstruct编译后的代码如下:

public class ExampleEnumMapperImpl implements ExampleEnumMapper {

    @Override
    public ExampleEnum map(ExampleMappingEnum exampleMappingEnum) {
        if (exampleMappingEnum == null) {
            return null;
        }

        ExampleEnum exampleEnum;

        switch (exampleMappingEnum) {
            case ENUM_MAPPING3: exampleEnum = ExampleEnum.ENUM3;
            break;
            case ENUM_MAPPING4: exampleEnum = ExampleEnum.ENUM3;
            break;
            case ENUM1: exampleEnum = ExampleEnum.ENUM1;
            break;
            case ENUM2: exampleEnum = ExampleEnum.ENUM2;
            break;
            default: throw new IllegalArgumentException( "Unexpected enum constant: " + exampleMappingEnum );
        }
        return exampleEnum;
    }
}

         假如不想像上述那样指定某几个枚举值映射到特定的枚举值,也可以直接使用MappingConstants进行默认映射。

@Mapper
public interface ExampleEnumMapper {

    ExampleEnumMapper INSTANCE = Mappers.getMapper(ExampleEnumMapper.class);

    @@ValueMapping(source = MappingConstants.ANY_REMAINING, target = "ENUM3")
    ExampleEnum map(ExampleMappingEnum exampleMappingEnum);

}

        通过MappingConstants,mapstruct会将所有未映射的值映射到ENUM3中。

四、总结

        本文主要讲解了mapstruct比较常见的使用方式。在最开始简单比较了下目前行业中的一些映射工具,然后介绍了下mapstruct的使用前准备工作,最后梳理了mapstruct一些比较常规的使用方式。当然mapstruct还有很多高级的用法,限于篇幅,就暂时不展开,可以参考mapstruct官网

参考

 

Installation – MapStructhttps://mapstruct.org/documentation/installation/

掘金https://juejin.cn/post/6992399204760944647

标签:UserDto,映射,mapstruct,UserMapper,private,梳理,用法,public
来源: https://blog.csdn.net/datastructure18/article/details/120400228

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有