在开发中你可曾遇到以下这样的问题?MyBtatis从数据库中查询的数据映射到domain的实体类上,而后有时候须要将domain的实体类映射给前端的VO类,用于展现。html
以下所示,假如Student是domain,而给前端展现的为StudentVO。
前端
有没有什么优雅的解决方式呢?可能你的第一反应就是使用Spring的BeanUtils.copyProperties (),可是BeanUtils.copyProperties ()只能转换类中字段名字同样且类型同样的字段。spring
因为BeanUtils.copyProperties ()采用的是反射,实际上当重复调用时效率是比较低的。(实际测试实际测试Spring的BeanUtils在生成 次数为1000000时须要1.6秒,而使用MapStruct仅须要69毫秒)。数据库
首先导入Maven依赖安全
<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>
首先domain的Studnet类和StudentVO类以下,能够看到字段是彻底一致的。其中@Data注解是lombok的表示含义以下,而@AllArgsConstructor则是提供全部参数的有参构造。微信
写一个Mapper接口StudentMapper,此处的Mapper注解不是MyBtais的Mapper注解。app
接下来测试一下,看一下生成的结果。less
Studnet类的age和name与StudentVO类的ageVO和nameVO对应不上时dom
在Mapper类中加入@Mapping的注解指定原对象的字段名和要被对应上的字段名。其中@Mappings表示多个字段须要对应,若是只是一个可使用@Mapping编辑器
接下来测试一下,看一下生成的结果。
某些时候,咱们的源不是一个,例如从数据库中查询出来了学生和老师,咱们须要将老师的名字给VO的name字段,学生的年龄给VO的age字段时可使用多参数源的映射方式。
在Mapper类的toStudentVO能够看到带了两个参数,而后在@Mapping中使用形参的名字去点字段的名。
接下来测试一下,看一下生成的结果。
有些时候咱们须要多层映射,例如老师类中有本身的一个老婆类(男老师),而后咱们须要将老师类中的老婆类的名字,赋值给VO,而年龄则使用学生的年龄。听上去怪怪的,就像学生有了老师的老婆😂 😂。
一样能够在Mapper类中使用符号"."的方式进行映射。
接下来测试一下,看一下生成的结果。
某些状况下,你须要不建立目标类型的新实例,而是更新该类型的现有实例的映射。能够经过为目标对象添加参数并使用@MappingTarget标记此参数来实现此类映射。
例如Student咱们将学生类的名字和年龄映射到VO中,可是不建立新的实例。
在Mapper接口中使用@MappingTarget注解,被@MappingTarget注解标记的实例将从未被标记中进行的实例中进行映射。
接下来测试一下,看一下生成的结果。
前面咱们在Mapper接口中代码中一直有一行代码,以下所示,是MapStruct为咱们提供的映射工厂,指定接口类型后自动帮咱们建立接口的实现,且保证是线程安全的单例,无需本身手动建立。
某些时候尤为是在作项目时,咱们用到了Sping,但愿映射后的新实例是交给Spring管理。这时候就须要进行依赖注入了。只须要在Mapper接口中的@Mapper注解中加入componentModel = "spring"便可。
映射属性在源对象和目标对象中具备相同的类型,这种状况不全有。例如,属性在源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的转换
输出结果以下所示
在映射集合的时候,咱们一样能够进行类型之间的转换,以下所示使用@MapMapping注解指定输出类型便可。
输出结果以下所示
固然MapStruct也支持其余各类类型的集合映射,上面只是举例了Map的映射
MapStruct支持生成将一个Java枚举类型映射到另外一个Java枚举类型的方法。默认状况下,源枚举中的每一个常量都映射到目标枚举类型中具备相同名称的常量。若是须要,可使用@ValueMapping注解将源枚举中的常量映射到具备其余名称的常量。源枚举中的几个常量能够映射到目标类型中的相同常量。
Student中是SexEnum枚举,而StudentVO中是Sex2Enum,且枚举中的值是一致时,咱们须要将Student中的映射到StudentVO中,此时只须要使用@Mapping来指定映射源和目标源的名称便可
当枚举值同样时,直接使用@Mapping来指定映射源和目标源的名称便可
当枚举值不一致时,使用@ValueMapping注解。
使用@ValueMapping注解,同时因为Student和StudentVO中的枚举类型不一致,因此以前的@Mapping注解也要使用。
有时候因为目标实例的构造方法被私有化后,咱们使用原来的方式没办法进行,缘由是MapStruct会在编译时去帮你实现,其中包含了调用构造方法。因此咱们能够定义工厂的形式来生成实例,而让MapStruct去调用工厂来生成实例,而再也不使用构造方法。
有咱们私有化了StudentVO的构造方法,若是直接使用MapStruct进行映射是会报错的。
指定工厂,同时在Mapper接口中的@Mapper注解上加入工厂的class
输出以下
在某些状况下,可能须要定制生成的映射方法,在目标对象中设置一个没法由MapStruct生成的方法实现时,可使用自定义映射来完成。假如咱们的StudentVO中的age是没法生成的。
首先定义类,而后实现Mapper接口,在重写的方法中写上须要的逻辑,且在Mapper接口中加入@DecorateWith注解,指定自定义映射的class。
测试输出结果,能够看到先给age值为0,最后输出为100.
上面的MapStruct只写了一些经常使用的,以及我以为可能会用到的,其中MapStruct还包含不少种用法,若是你想彻底的了解他的全部功能,能够参考MapStruct的官方文档,文档地址能够在最下面能够看到。
文档地址:
http://mapstruct.org/documentation/stable/reference/html/
本文分享自微信公众号 - 大猫的Java笔记(damaoJava)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。