mapstruct idea插件,mapstruct使用教程
实体的属性映射怎么会丢失呢?
众所周知,随着一个项目的成熟,模块划分会越来越细,其中实体类一般存储在域中,但域项目最好不要依赖于其他项目。所以其他项目想要获取实体类数据,需要在各自的项目中编写模型。自定义模型可以根据自己的业务需求映射相应的实体属性。这样看来,这个测绘工程似乎并不简单。萨姆几乎被弄糊涂了.
00-1010所以Amio今天要给大家一个安利的叫mapstruct的插件,专门用来处理domin实体类和模型类之间的属性映射。我们只需要定义mapper接口,mapstruct会在编译的时候自动帮我们实现这个映射接口,避免了繁琐复杂的映射实现。
可能有些朋友要问了?为什么不使用BeanUtils的copyProperties方法?难道不能实现属性映射吗?
啊,阿苗,一开始很好奇,于是和BeanUtils进行了深入的交流。最后我发现BeanUtils是一个lout,只能用属性映射,或者属性相同的情况下,允许映射对象的属性更少;但是,当映射的属性数据类型或映射的字段名称被修改时,映射将失败。另一方面,Mapstruct是个聪明的妻子。她心思细腻,考虑到我们可能遇到的所有情况。(要是能找到这样的老婆就好了,阿苗,她笑着对猪的声音说。)
以下是这个插件的开源项目地址和各种例子:
Github地址:https://github.com/mapstruct/mapstruct/使用实例:https://github.com/mapstruct/mapstruct-examples
序
接下来,阿苗将和大家一起去揭开这位聪明妻子的真实面纱,所以我们还是要做一些准备工作。00-1010从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属性用于指定自动生成的接口实现类的组件类型。该属性支持四个值:
:这是默认的:情况。mapstruct不使用任何组件类型,可以通过Mappers.getMapper(Class)获取自动生成的实例对象。CDI :生成的映射器是一个应用程序范围的CDI bean,可以通过@ InjectSpring 3360检索。它会自动向实现类添加一个@Component注释。可以用Spring的@Autowired方式注入。@javax.inject.Named和@Singleton注释将被添加到jsr330:生成的实现类中。可以通过@Inject注释得到
一、准备工作
。首先,您需要导入依赖包,它主要由两个包组成:Org.mapstruct:mapstruct:包含了一些必要的注释,比如@Mapping。r如果我们使用高于1.8的JDK版本,当我们将依赖项导入pom时,建议的坐标是:org . map struct 3360 map struct-JDK 8,这可以帮助我们利用Java8的一些新特性。org . map struct 3360 map struct-processor:注释处理器,根据注释自动生成mapper的实现。从属groupIdorg.mapstruct/groupId!-JDK8使用下面的map struct-artifactidmap struct-JDK 8/Artifactid version1.2.0.Final/version/de
pendency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.2.0.Final</version> </dependency>好了,准备工作做完了,接下来我们就看看巧媳妇儿巧在什么地方吧。
二、先简单玩一把
2.1、定义实体类以及被映射类
// 实体类@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic class User { private Integer id; private String name; private String createTime; private LocalDateTime updateTime;}// 被映射类VO1:和实体类一模一样@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic class UserVO1 { private Integer id; private String name; private String createTime; private LocalDateTime updateTime;}// 被映射类VO1:比实体类少一个字段@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic 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 接口的实现。
2.3、使用
@RestControllerpublic class TestController { @GetMapping("convert") public Object convertEntity() { User user = User.builder() .id(1) .name("张三") .createTime("2020-04-01 11:05:07") .updateTime(LocalDateTime.now()) .build(); List<Object> objectList = new ArrayList<>(); objectList.add(user); // 使用mapstruct UserVO1 userVO1 = UserCovertBasic.INSTANCE.toConvertVO1(user); objectList.add("userVO1:" + UserCovertBasic.INSTANCE.toConvertVO1(user)); objectList.add("userVO1转换回实体类user:" + UserCovertBasic.INSTANCE.fromConvertEntity1(userVO1)); // 输出转换结果 objectList.add("userVO2:" + " " + UserCovertBasic.INSTANCE.toConvertVO2(user)); // 使用BeanUtils UserVO2 userVO22 = new UserVO2(); BeanUtils.copyProperties(user, userVO22); objectList.add("userVO22:" + " " + userVO22); return objectList; }}
2.4、查看编译结果
通过IDE的反编译功能查看编译后自动生成UserCovertBasic
的实现类 UserCovertBasicImpl
,内容如下:
@Componentpublic class UserCovertBasicImpl implements UserCovertBasic { public UserCovertBasicImpl() { } public UserVO1 toConvertVO1(User source) { if (source == null) { return null; } else { UserVO1 userVO1 = new UserVO1(); userVO1.setId(source.getId()); userVO1.setName(source.getName()); userVO1.setCreateTime(source.getCreateTime()); userVO1.setUpdateTime(source.getUpdateTime()); return userVO1; } } public User fromConvertEntity1(UserVO1 userVO1) { if (userVO1 == null) { return null; } else { User user = new User(); user.setId(userVO1.getId()); user.setName(userVO1.getName()); user.setCreateTime(userVO1.getCreateTime()); user.setUpdateTime(userVO1.getUpdateTime()); return user; } } public UserVO2 toConvertVO2(User source) { if (source == null) { return null; } else { UserVO2 userVO2 = new UserVO2(); userVO2.setId(source.getId()); userVO2.setName(source.getName()); userVO2.setCreateTime(source.getCreateTime()); return userVO2; } }}
2.5、浏览器查看结果
好了,一个流程就走完了,是不是感觉贼简单呢?
而且呀,阿淼温馨提醒:如果是要转换一个集合的话,只需要把这里的实体类换成集合就行了,例如:
List<UserVO1> toConvertVOList(List<User> source);
三、不简单的情况
上面已经把整个流程都给过了一遍了,相信大家对mapstruct
也有了一个基础的了解了,所以接下来的情况我们就不展示全部代码了,毕竟篇幅也有限,所以就直接上关键代码(因为不关键的和上面内容一样,哈哈)
3.1、类型不一致
实体类我们还是沿用User
;被映射对象 UserVO3
改为:
@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic class UserVO3 { private String id; private String name; // 实体类该属性是String private LocalDateTime createTime; // 实体类该属性是LocalDateTime private String updateTime;}那么我们定义的接口就要稍稍修改一下了:
@Mappings({ @Mapping(target = "createTime", expression = "java(com.java.mmzsblog.util.DateTransform.strToDate(source.getCreateTime()))"), }) UserVO3 toConvertVO3(User source); User fromConvertEntity3(UserVO3 userVO3);上面
expression
指定的表达式内容如下:
public class DateTransform { public static LocalDateTime strToDate(String str){ DateTimeFormatter df = DateTimeFormatter.ofPattern("yyy-MM-dd HH:mm:ss"); return LocalDateTime.parse("2018-01-12 17:07:05",df); }}通过IDE的反编译功能查看编译后的实现类,结果是这样子的:
从图中我们可以看到,编译时使用了expression中定义的表达式对目标字段 createTime
进行了转换;然后你还会发现 updateTime
字段也被自动从 LocalDateTime 类型转换成了 String 类型。
阿淼小结:
当字段类型不一致时,以下的类型之间是mapstruct
自动进行类型转换的: 1、基本类型及其他们对应的包装类型。此时 mapstruct
会自动进行拆装箱。不需要人为的处理2、基本类型的包装类型和string类型之间除此之外的类型转换我们可以通过定义表达式来进行指定转换。
3.2、字段名不一致
实体类我们还是沿用User
;被映射对象 UserVO4
改为:
@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic class UserVO4 { // 实体类该属性名是id private String userId; // 实体类该属性名是name private String userName; private String createTime; private String updateTime;}那么我们定义的接口就要稍稍修改一下了:
@Mappings({ @Mapping(source = "id", target = "userId"), @Mapping(source = "name", target = "userName") }) UserVO4 toConvertVO(User source); User fromConvertEntity(UserVO4 userVO4);通过IDE的反编译功能查看编译后的实现类,编译后的结果是这样子的:
很明显, mapstruct
通过读取我们配置的字段名对应关系,帮我们把它们赋值在了相对应的位置上,可以说是相当优秀了,但这也仅仅是优秀,而更秀的还请继续往下看:
阿淼小结:
当字段名不一致时,通过使用 @Mappings
注解指定对应关系,编译后即可实现对应字段的赋值。
3.3、属性是枚举类型
实体类我们还是改用UserEnum
:
@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic class UserEnum { private Integer id; private String name; private UserTypeEnum userTypeEnum;}被映射对象
UserVO5
改为:
@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic class UserVO5 { private Integer id; private String name; private String type;}枚举对象是:
@Getter@AllArgsConstructorpublic enum UserTypeEnum { Java("000", "Java开发工程师"), DB("001", "数据库管理员"), LINUX("002", "Linux运维员"); private String value; private String title;}那么我们定义的接口还是照常定义,不会受到它是枚举就有所变化:
@Mapping(source = "userTypeEnum", target = "type") UserVO5 toConvertVO5(UserEnum source); UserEnum fromConvertEntity5(UserVO5 userVO5);通过IDE的反编译功能查看编译后的实现类,编译后的结果是这样子的:
很明显, mapstruct
通过枚举类型的内容,帮我们把枚举类型转换成字符串,并给type赋值,可谓是小心使得万年船啊。看来这巧媳妇儿不仅仅优秀还心细啊……
源代码
文章中的所有例子已上传github:https://github.com/mmzsblog/mapstructDemo到此这篇关于详解Java中的mapstruct使用的文章就介绍到这了,更多相关Javamapstruct使用内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。