序
所以阿粉今天就要给大家安利一款叫 mapstruct
的插件,它就是专门用来处理 domin 实体类与 model 类的属性映射的,我们只需定义 mapper 接口,mapstruct 在编译的时候就会自动的帮我们实现这个映射接口,避免了麻烦复杂的映射实现。
那可能有的小伙伴就要问了?为啥不用 BeanUtils
的 copyProperties
方法呢?不也照样可以实现属性的映射么?
这个啊,阿粉我开始也是好奇,所以就和 BeanUtils
深入交流了一番,最后才发现,BeanUtils
就是一个大老粗,只能同属性映射,或者在属性相同的情况下,允许被映射的对象属性少;但当遇到被映射的属性数据类型被修改或者被映射的字段名被修改,则会导致映射失败。而 mapstruct
就是一个巧媳妇儿了,她心思细腻,把我们可能会遇到的情况都给考虑到了(要是阿粉我也能找一个这样的媳妇儿该多好,内心笑出了猪声)
如下是这个插件的开源项目地址和各种例子:
- Github地址:https://github.com/mapstruct/mapstruct/
- 使用例子:https://github.com/mapstruct/mapstruct-examples
一、准备工作
接下来,阿粉将和大家一起去解开这个巧媳妇儿的真正面纱,所以我们还需要做一点准备工作。
1.1、了解@Mapper
注解
从 mybatis3.4.0 开始加入的 @Mapper 注解,目的就是为了不再写mapper映射文件。
我们只需要在 dao 层定义的接口上使用注解就可以实现sql语句的编写,例如:
@Select("select * from user where name = #{name}")public User find(String name);
如上就是一个简单的使用,虽然简单,但也确实体现出了这个注解的优越性,至少少写了一个xml文件。
但阿粉我今天可不是想跟你探讨 @Mapper
注解,我主要是想去看我的巧媳妇儿 mapstruct
,所以我就只是想说下 @Mapper
注解的 componentModel
属性,componentModel
属性用于指定自动生成的接口实现类的组件类型,这个属性支持四个值:
- default: 这是默认的情况,mapstruct 不使用任何组件类型, 可以通过Mappers.getMapper(Class)方式获取自动生成的实例对象。
- cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
- spring: 生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入
- jsr330: 生成的实现类上会添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取
1.2、依赖包
首先需要把依赖包导入,主要由两个包组成:
org.mapstruct:mapstruct
:包含了一些必要的注解,例如@Mapping。r若我们使用的JDK版本高于1.8,当我们在pom里面导入依赖时候,建议使用坐标是:org.mapstruct:mapstruct-jdk8
,这可以帮助我们利用一些Java8的新特性。org.mapstruct:mapstruct-processor
:注解处理器,根据注解自动生成mapper的实现。
<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.1、定义实体类以及被映射类
// 实体类 @Data @NoArgsConstructor @AllArgsConstructor @Builder public class User { private Integer id; private String name; private String createTime; private LocalDateTime updateTime; } // 被映射类VO1:和实体类一模一样 @Data @NoArgsConstructor @AllArgsConstructor @Builder public class UserVO1 { private Integer id; private String name; private String createTime; private LocalDateTime updateTime; } // 被映射类VO1:比实体类少一个字段 @Data @NoArgsConstructor @AllArgsConstructor @Builder public class UserVO2 { private Integer id; private String name; private String createTime; }
2.2、定义接口:
当实体类和被映射对象属性相同或者被映射对象属性值少几个时:
@Mapper(componentModel = "spring") public interface UserCovertBasic { UserCovertBasic INSTANCE = Mappers.getMapper(UserCovertBasic.class); /** * 字段数量类型数量相同,利用工具BeanUtils也可以实现类似效果 * @param source * @return */ UserVO1 toConvertVO1(User source); User fromConvertEntity1(UserVO1 userVO1); /** * 字段数量类型相同,数量少:仅能让多的转换成少的,故没有fromConvertEntity2 * @param source * @return */ UserVO2 toConvertVO2(User source); }
从上面的代码可以看出:接口中声明了一个成员变量INSTANCE,母的是让客户端可以访问 Mapper 接口的实现。