点击上方蓝色“方志朋”,选择“设为星标”

回复“666”获取独家整理的学习资料!

前言

按照日常开发习惯,对于不同领域层使用不同JavaBean对象传输数据,避免相互影响,因此基于数据库实体对象User衍生出比如UserDto、UserVo等对象,于是在不同层之间进行数据传输时,不可避免地需要将这些对象进行互相转换操作。

常见的转换方式有:

  • 调用getter/setter方法进行属性赋值

  • 调用BeanUtil.copyPropertie进行反射属性赋值

第一种方式不必说,属性多了就需要写一大坨getter/setter代码。第二种方式比第一种方式要简便很多,但是坑巨多,比如sources与target写反,难以定位某个字段在哪里进行的赋值,同时因为用到反射,导致性能也不佳。

鉴于此,今天写一写第三种对象转换方式,本文使用的是 MapStruct 工具进行转换,MapStruct 原理也很简单,就是在代码编译阶段生成对应的赋值代码,底层原理还是调用getter/setter方法,但是这是由工具替我们完成,MapStruct在不影响性能的情况下,解决了前面两种方式弊端,很赞~

准备工作

为了讲解 MapStruct 工具的使用,本文使用常见的 User 类以及对应 UserDto 对象来演示。

@Data
@Accessors(chain = true)
public class User {private Long id;private String username;private String password; private Integer sex; private LocalDate birthday; private LocalDateTime createTime; private String config; private String test;
}@Data
@Accessors(chain = true)
public class UserVo {private Long id;private String username;private String password;private Integer gender;private LocalDate birthday;private String createTime;private List<UserConfig> config;private String test; @Datapublic static class UserConfig {private String field1;private Integer field2;}
}

注意观察这两个类的区别。

一、MapStruct 配置以及基础使用

项目中引入 MapStruct 的依赖

<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.3.1.Final</version>
</dependency>
<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.3.1.Final</version>
</dependency>

因为项目中的对象转换操作基本都一样,因此抽取除了一个转换基类,不同对象如果只是简单转换可以直接继承该基类,而无需覆写基类任何方法,即只需要一个空类即可。如果子类覆写了基类的方法,则基类上的 @Mapping 会失效。

@MapperConfig
public interface BaseMapping<SOURCE, TARGET> {/*** 映射同名属性*/@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")TARGET sourceToTarget(SOURCE var1);/*** 反向,映射同名属性*/@InheritInverseConfiguration(name = "sourceToTarget")SOURCE targetToSource(TARGET var1);/*** 映射同名属性,集合形式*/@InheritConfiguration(name = "sourceToTarget")List<TARGET> sourceToTarget(List<SOURCE> var1);/*** 反向,映射同名属性,集合形式*/@InheritConfiguration(name = "targetToSource")List<SOURCE> targetToSource(List<TARGET> var1);/*** 映射同名属性,集合流形式*/List<TARGET> sourceToTarget(Stream<SOURCE> stream);/*** 反向,映射同名属性,集合流形式*/List<SOURCE> targetToSource(Stream<TARGET> stream);
}

实现 User 与 UserVo 对象的转换器

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;@Mapper(componentModel = "spring")
public interface UserMapping extends BaseMapping<User, UserVo> {@Mapping(target = "gender", source = "sex")@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")@OverrideUserVo sourceToTarget(User var1);@Mapping(target = "sex", source = "gender")@Mapping(target = "password", ignore = true)@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")@OverrideUser targetToSource(UserVo var1);default List<UserConfig> strConfigToListUserConfig(String config) {return JSON.parseArray(config, UserConfig.class);}default String listUserConfigToStrConfig(List<UserConfig> list) {return JSON.toJSONString(list);}
}

本文示例使用的是 Spring 的方式,@Mapper 注解的 componentModel 属性值为 spring,不过应该大多数都用的此模式进行开发。

@Mapping用于配置对象的映射关系,示例中 User 对象性别属性名为 sex,而UserVo对象性别属性名为gender,因此需要配置 target 与 source 属性。

password 字段不应该返回到前台,可以采取两种方式不进行转换,第一种就是在vo对象中不出现password字段,第二种就是在@Mapping中设置该字段 ignore = true。

MapStruct 提供了时间格式化的属性 dataFormat,支持Date、LocalDate、LocalDateTime等时间类型与String的转换。示例中birthday 属性为 LocalDate 类型,可以无需指定dataFormat自动完成转换,而LocalDateTime类型默认使用的是ISO格式时间,在国内往往不符合需求,因此需要手动指定一下 dataFormat。

二、自定义属性类型转换方法

一般常用的类型字段转换 MapStruct都能替我们完成,但是有一些是我们自定义的对象类型,MapStruct就不能进行字段转换,这就需要我们编写对应的类型转换方法,笔者使用的是JDK8,支持接口中的默认方法,可以直接在转换器中添加自定义类型转换方法。

示例中User对象的config属性是一个JSON字符串,UserVo对象中是List类型的,这需要实现JSON字符串与对象的互转。

default List<UserConfig> strConfigToListUserConfig(String config) {return JSON.parseArray(config, UserConfig.class);
}default String listUserConfigToStrConfig(List<UserConfig> list) {return JSON.toJSONString(list);
}

如果是 JDK8以下的,不支持默认方法,可以另外定义一个 转换器,然后再当前转换器的 @Mapper 中通过 uses = XXX.class 进行引用。

定义好方法之后,MapStruct当匹配到合适类型的字段时,会调用我们自定义的转换方法进行转换。

三、单元测试

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class MapStructTest {@Resourceprivate UserMapping userMapping;@Testpublic void tetDomain2DTO() {User user = new User().setId(1L).setUsername("zhangsan").setSex(1).setPassword("abc123").setCreateTime(LocalDateTime.now()).setBirthday(LocalDate.of(1999, 9, 27)).setConfig("[{\"field1\":\"Test Field1\",\"field2\":500}]");UserVo userVo = userMapping.sourceToTarget(user);log.info("User: {}", user);log.info("UserVo: {}", userVo);}@Testpublic void testDTO2Domain() {UserConfig userConfig = new UserConfig();userConfig.setField1("Test Field1");userConfig.setField2(500);UserVo userVo = new UserVo().setId(1L).setUsername("zhangsan").setGender(2).setCreateTime("2020-01-18 15:32:54").setBirthday(LocalDate.of(1999, 9, 27)).setConfig(Collections.singletonList(userConfig));User user = userMapping.targetToSource(userVo);log.info("UserVo: {}", userVo);log.info("User: {}", user);}

四、常见问题

当两个对象属性不一致时,比如User对象中某个字段不存在与UserVo当中时,在编译时会有警告提示,可以在@Mapping中配置 ignore = true,当字段较多时,可以直接在@Mapper中设置unmappedTargetPolicy属性或者unmappedSourcePolicy属性为 ReportingPolicy.IGNORE即可。

如果项目中也同时使用到了 Lombok,一定要注意 Lombok的版本要等于或者高于1.18.10,否则会有编译不通过的情况发生,笔者掉进这个坑很久才爬了出来,希望各位不要重复踩坑。

代码下载

本文涉及代码已上传到 Github,以供参考。

https://github.com/Mosiki/learning-modules/tree/master/learning-mapstruct

参考

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

https://mapstruct.org/faq/

https://github.com/mapstruct/mapstruct-examples

http://www.kailing.pub/MapStruct1.3/index.html

https://www.cnblogs.com/javaguide/p/11861749.html

热门内容:
  • Mybatis 使用的 9 种设计模式,真是太有用了

  • 你的登录接口真的安全吗?

  • 微服务架构:注册中心 ZooKeeper、Eureka、Consul 、Nacos 对比!

  • 图解ElasticSearch 搜索原理

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡

推荐一款 Java 对象映射神器相关推荐

  1. java反向映射_推荐一款Java对象映射神器,别再傻傻手动转换了!

    点击蓝色字免费订阅,每天收到这样的好信息 前言:最近有不少粉丝关注本公众号.并且我已经成功开通了流量主同时会赚一点点广告费,我打算每个月把这部分钱拿出来给大家买点书刊,算是给大家一点福利吧.大家想买什 ...

  2. # 对象json互相转换_推荐一款 Java 对象映射神器

    阅读本文大概需要 5 分钟. 来自:网络 前言按照日常开发习惯,对于不同领域层使用不同JavaBean对象传输数据,避免相互影响,因此基于数据库实体对象User衍生出比如UserDto.UserVo等 ...

  3. 推荐一款接口 API 设计神器!

    今天栈长给大家推荐一款接口 API 设计神器,传说中的,牛逼哄洪的 Swagger,它到底是什么?今天为大家揭开谜底! Swagger是什么? 官网:https://swagger.io/ Swagg ...

  4. 推荐一款万能抓包神器:Fiddler Everywhere

    搞IT技术的同行,相信没有几个人是不会抓包这项技能的(如果很不幸你中枪了,那希望这篇文章给你一些动力),市面上的抓包工具也有很多,常用的有:Charles.Fiddler.Burpsuite.Wire ...

  5. 推荐一款全能测试开发神器!1分钟快速上手!

    关注上方"测试开发技术",选择星标, 干货技术,第一时间送达! 1. 说一下背景 在日常开发或者测试工作中,经常会因为下游服务不可用或者不稳定时,通过工具或者技术手段去模拟一个HT ...

  6. 黑科技:进阶必备,推荐两款自动生成代码神器

    关注上方"测试开发技术",选择星标, 干货技术,第一时间送达! 最近两年,在互联网行业各大技术峰会上,都能看到关于工程效能这个概念,从侧面也反映出了研发效能已经逐渐被各企业所重视! ...

  7. 低代码开发,推荐一款Web 端自动化神器:Automa!

    1. Automa介绍 又到了优秀工具推荐的时候了,今天给大家分享一款前端自动化操作神器: Automa. 首先了解一下Automa是什么?Automa它定位是一款 Chrome 插件,也就意味着,它 ...

  8. 低代码开发,推荐一款Web 端自动化神器:Automa

    1. Automa介绍 又到了优秀工具推荐的时候了,今天给大家分享一款前端自动化操作神器: Automa. 首先了解一下Automa是什么? Automa它定位是一款 Chrome 插件,也就意味着, ...

  9. 推荐一款超强接口管理神器 Apifox

    去年,在公众号给大家推荐了一款新面市不久的接口测试神器:Apifox,如果还未了解的读者,感兴趣的话可查阅原文:推荐一款技术人必备的接口测试神器:Apifox 为了照顾新进来的读者,且最近一年,Api ...

最新文章

  1. Docker 是什么?
  2. Windows server2008修改远程桌面端口的方法
  3. chrome 技术篇-控制台切换框架,切换dom操作区
  4. one-hot与哑变量(dummy variable)的区别
  5. hdu 4150 Powerful Incantation
  6. 单元测试中Assert类的用法
  7. 17 操作系统第四章 磁盘管理 磁盘的结构 磁盘调度算法 减少磁盘延迟时间的方法
  8. ios自定义UITextView 支持placeholder的方法
  9. 前缀,中缀,后缀表达式求值
  10. 前置放大器和功率放大器的组成结构及功能原理是什么?
  11. ESP8266-Arduino编程实例-BMI160惯性测量传感器驱动
  12. 计算机专业对于英语水平的要求,英语对计算机专业的重要性及如何提高英语水平...
  13. CE修改器入门:寻找指针基址
  14. javascript下载图片而不是浏览器默认打开图片
  15. 【STM32训练—TOF激光测距模块】第一篇、STM32驱动TOF10120测量距离
  16. vue.runtime.esm.js?2b0e:619 [Vue warn]: Property “XX“ must be accessed with “$data._tos“ because
  17. 一次ffmpeg -c copy 在avformat_write_header函数处报错:Could not find tag for codec rawvideo in stream #0的原因分析
  18. PMP模拟试题与解析(四)
  19. Python安装Numpy模块
  20. 设计模式—— 四:接口隔离原则

热门文章

  1. BZOJ.5249.[九省联考2018]iiidx(贪心 线段树)
  2. 【校招面试 之 C/C++】第16题 C++ new和delete的实现原理
  3. Linux Linux程序练习十一(网络编程大文件发送UDP版)
  4. Leetcode 之Binary Tree Postorder Traversal(44)
  5. Test execution order
  6. 自己用C#写一个采集器、蜘蛛(zz)
  7. Strategy_Level2
  8. SIFT 特征检测及匹配
  9. 【Codeforces】913C Party Lemonade (贪...)。
  10. android 获取视频大小,Android 获取视频缩略图(获取视频每帧数据)的优化方案