前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >啥?你居然不知道MapStruct

啥?你居然不知道MapStruct

作者头像
大猫的Java笔记
发布2020-10-29 16:29:12
2.3K0
发布2020-10-29 16:29:12
举报
文章被收录于专栏:大猫的Java笔记

1.为什么使用MapStruct

在开发中你可曾遇到如下这样的问题?MyBtatis从数据库中查询的数据映射到domain的实体类上,然后有时候需要将domain的实体类映射给前端的VO类,用于展示。

如下所示,假如Student是domain,而给前端展示的为StudentVO。

有没有什么优雅的解决方式呢?可能你的第一反应就是使用Spring的BeanUtils.copyProperties (),但是BeanUtils.copyProperties ()只能转换类中字段名字一样且类型一样的字段。

由于BeanUtils.copyProperties ()采用的是反射,实际上当重复调用时效率是比较低的。(实际测试实际测试Spring的BeanUtils在生成 次数为1000000时需要1.6秒,而使用MapStruct仅需要69毫秒)。

2.MapStruct的依赖

首先导入Maven依赖

代码语言:javascript
复制
<dependency>
    <groupId>org.mapstruct</groupId>
    <!-- jdk8以下就使用mapstruct -->
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.2.0.Final</version>
 </dependency>
 
 <dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
     <version>1.2.0.Final</version>
 </dependency>

2.当成员变量名相同时的使用

首先domain的Studnet类和StudentVO类如下,可以看到字段是完全一致的。其中@Data注解是lombok的表示含义如下,而@AllArgsConstructor则是提供所有参数的有参构造。

写一个Mapper接口StudentMapper,此处的Mapper注解不是MyBtais的Mapper注解。

接下来测试一下,看一下生成的结果。

2.成员变量名不相同时的使用

Studnet类的age和name与StudentVO类的ageVO和nameVO对应不上时

在Mapper类中加入@Mapping的注解指定原对象的字段名和要被对应上的字段名。其中@Mappings表示多个字段需要对应,如果只是一个可以使用@Mapping

接下来测试一下,看一下生成的结果。

3.多参数源映射

某些时候,我们的源不是一个,例如从数据库中查询出来了学生和老师,我们需要将老师的名字给VO的name字段,学生的年龄给VO的age字段时可以使用多参数源的映射方式。

在Mapper类的toStudentVO可以看到带了两个参数,然后在@Mapping中使用形参的名字去点字段的名。

接下来测试一下,看一下生成的结果。

3.多层嵌套映射

有些时候我们需要多层映射,例如老师类中有自己的一个老婆类(男老师),然后我们需要将老师类中的老婆类的名字,赋值给VO,而年龄则使用学生的年龄。听上去怪怪的,就像学生有了老师的老婆? ?。

同样可以在Mapper类中使用符号"."的方式进行映射。

接下来测试一下,看一下生成的结果。

4.更新现有的Bean

某些情况下,你需要不创建目标类型的新实例,而是更新该类型的现有实例的映射。可以通过为目标对象添加参数并使用@MappingTarget标记此参数来实现此类映射。

例如Student我们将学生类的名字和年龄映射到VO中,但是不创建新的实例。

在Mapper接口中使用@MappingTarget注解,被@MappingTarget注解标记的实例将从未被标记中进行的实例中进行映射。

接下来测试一下,看一下生成的结果。

5.映射器工厂

前面我们在Mapper接口中代码中一直有一行代码,如下所示,是MapStruct为我们提供的映射工厂,指定接口类型后自动帮我们创建接口的实现,且保证是线程安全的单例,无需自己手动创建。

6.依赖注入

某些时候尤其是在做项目时,我们用到了Sping,希望映射后的新实例是交给Spring管理。这时候就需要进行依赖注入了。只需要在Mapper接口中的@Mapper注解中加入componentModel = "spring"即可。

7.数据类型转换

映射属性在源对象和目标对象中具有相同的类型,这种情况不全有。例如,属性在源bean中可以是int类型,但在目标bean中可以是Long类型。另一个例子是对其他对象的引用,这些对象应该映射到目标模型中的相应类型。例如:Teachr类可能有一个Wife类型的属性wife,在映射VO对象时需要将其转换为StudentVO对象。

在许多情况下,MapStruct会自动处理类型转换。例如,如果属性在源bean中的类型为int,但在目标bean中的类型为String,则生成的代码将分别通过调用String.valueOf(int)和Integer.parseInt(String)来透明地执行转换。

通过案例来实现从int转换为String 从BigDecimal到String的转换 以及从Date到String的转换

输出结果如下所示

8.映射集合

在映射集合的时候,我们同样可以进行类型之间的转换,如下所示使用@MapMapping注解指定输出类型即可。

输出结果如下所示

当然MapStruct也支持其他各种类型的集合映射,上面只是举例了Map的映射

9.映射枚举

MapStruct支持生成将一个Java枚举类型映射到另一个Java枚举类型的方法。默认情况下,源枚举中的每个常量都映射到目标枚举类型中具有相同名称的常量。如果需要,可以使用@ValueMapping注解将源枚举中的常量映射到具有其他名称的常量。源枚举中的几个常量可以映射到目标类型中的相同常量。

Student中是SexEnum枚举,而StudentVO中是Sex2Enum,且枚举中的值是一致时,我们需要将Student中的映射到StudentVO中,此时只需要使用@Mapping来指定映射源和目标源的名称即可

当枚举值一样时,直接使用@Mapping来指定映射源和目标源的名称即可

当枚举值不一致时,使用@ValueMapping注解。

使用@ValueMapping注解,同时由于Student和StudentVO中的枚举类型不一致,所以之前的@Mapping注解也要使用。

10.对象工厂

有时候由于目标实例的构造方法被私有化后,我们使用原来的方式没办法进行,原因是MapStruct会在编译时去帮你实现,其中包含了调用构造方法。所以我们可以定义工厂的形式来生成实例,而让MapStruct去调用工厂来生成实例,而不再使用构造方法。

有我们私有化了StudentVO的构造方法,如果直接使用MapStruct进行映射是会报错的。

指定工厂,同时在Mapper接口中的@Mapper注解上加入工厂的class

输出如下

11.自定义映射

在某些情况下,可能需要定制生成的映射方法,在目标对象中设置一个无法由MapStruct生成的方法实现时,可以使用自定义映射来完成。假如我们的StudentVO中的age是无法生成的。

首先定义类,然后实现Mapper接口,在重写的方法中写上需要的逻辑,且在Mapper接口中加入@DecorateWith注解,指定自定义映射的class。

测试输出结果,可以看到先给age值为0,最后输出为100.

上面的MapStruct只写了一些常用的,以及我觉得可能会用到的,其中MapStruct还包含很多种用法,如果你想完全的了解他的所有功能,可以参考MapStruct的官方文档,文档地址可以在最下面可以看到。

文档地址:

http://mapstruct.org/documentation/stable/reference/html/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大猫的Java笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档