点击蓝色字免费订阅,每天收到这样的好信息

前言:最近有不少粉丝关注本公众号。并且我已经成功开通了流量主同时会赚一点点广告费,我打算每个月把这部分钱拿出来给大家买点书刊,算是给大家一点福利吧。大家想买什么书扫描下方的加他拉你加群。最后,非常感谢大家的关注。

私活接单qq群:716817407

前几天发的《一份热乎的 SpringBoot 前后端分离后台管理系统分析!分模块开发、RBAC 权限控制...》这篇文章中我推荐了 MapStruct 来做对象映射。这篇文章就带着小伙伴们详细的看一下这个好用的工具库。

前言

按照日常开发习惯,对于不同领域层使用不同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; // 其他扩展信息,以JSON格式存储

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 config;

private String test; // 测试字段

@Data

public static class UserConfig{

private String field1;

private Integer field2;

}

}

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

一、MapStruct 配置以及基础使用

项目中引入 MapStruct 的依赖

org.mapstruct

mapstruct

1.3.1.Final

org.mapstruct

mapstruct-processor

1.3.1.Final

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

@MapperConfig

public interface BaseMapping{

/**

* 映射同名属性

*/

@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 sourceToTarget(List var1);

/**

* 反向,映射同名属性,集合形式

*/

@InheritConfiguration(name = "targetToSource")

List targetToSource(List var1);

/**

* 映射同名属性,集合流形式

*/

List sourceToTarget(Stream stream);

/**

* 反向,映射同名属性,集合流形式

*/

List targetToSource(Stream stream);

}

实现 User 与 UserVo 对象的转换器

import org.mapstruct.Mapper;

import org.mapstruct.Mapping;

@Mapper(componentModel = "spring")

public interface UserMapping extends BaseMapping{

@Mapping(target = "gender", source = "sex")

@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")

@Override

UserVo sourceToTarget(User var1);

@Mapping(target = "sex", source = "gender")

@Mapping(target = "password", ignore = true)

@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")

@Override

User targetToSource(UserVo var1);

default List strConfigToListUserConfig(String config){

return JSON.parseArray(config, UserConfig.class);

}

default String listUserConfigToStrConfig(List 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 strConfigToListUserConfig(String config){

return JSON.parseArray(config, UserConfig.class);

}

default String listUserConfigToStrConfig(List list){

return JSON.toJSONString(list);

}

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

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

三、单元测试

@Slf4j

@RunWith(SpringRunner.class)

@SpringBootTest

public class MapStructTest{

@Resource

private UserMapping userMapping;

@Test

public 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);

//        User: User(id=1, username=zhangsan, password=abc123, sex=1, birthday=1999-09-27, createTime=2020-01-17T17:46:20.316, config=[{"field1":"Test Field1","field2":500}])

log.info("UserVo: {}", userVo);

//        UserVo: UserVo(id=1, username=zhangsan, gender=1, birthday=1999-09-27, createTime=2020-01-17 17:46:20, config=[UserVo.UserConfig(field1=Test Field1, field2=500)])

}

@Test

public 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);

//        UserVo: UserVo(id=1, username=zhangsan, gender=2, birthday=1999-09-27, createTime=2020-01-18 15:32:54, config=[UserVo.UserConfig(field1=Test Field1, field2=500)])

log.info("User: {}", user);

//        User: User(id=1, username=zhangsan, password=null, sex=2, birthday=1999-09-27, createTime=2020-01-18T15:32:54, config=[{"field1":"Test Field1","field2":500}])

}

四、常见问题

当两个对象属性不一致时,比如

User对象中某个字段不存在与

UserVo当中时,在编译时会有警告提示,可以在

@Mapping中配置

ignore = true,当字段较多时,可以直接在

@Mapper中设置

unmappedTargetPolicy属性或者

unmappedSourcePolicy属性为

ReportingPolicy.IGNORE即可。

如果项目中也同时使用到了 Lombok,一定要注意 Lombok 的版本要等于或者高于

1.18.10,否则会有编译不通过的情况发生,笔者掉进这个坑很久才爬了出来,希望各位不要重复踩坑。

代码下载

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

mapstruct 最佳实践示例代码

[1]

打油诗

我不在乎我的作品文章是被现在的人读还是由子孙后代来读。既然上帝花了六千年来等一位观察者,我可以花上一个世纪来等待读者。

私活接单qq群:716817407

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

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

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

  2. swagger 扫描java文档_推荐一款在运行时通过javadoc生成Swagger API文档的库

    介绍 一般,我们使用Springfox生成swagger api文档,但Springfox不支持从javadoc中生成,只能通过注解的方式标注文档. 这样,当共享一些POJO类时,为了同时生成java ...

  3. java 开源监控_推荐一款强大的国产开源监控系统!

    项目简介 集监控点监控.日志监控.数据可视化以及监控告警为一体的国产开源监控系统,直接部署即可使用. 监控数据类型丰富,提供多种富有表现力的图表,满足对数据可视化的需要,目前支持折线图.饼图.地理位置 ...

  4. 会员系统 java源码_推荐会员管理系统JAVA源码

    [实例简介] 会员管理系统 struts+hibernate+spring 完整,可二次开发 推荐 [实例截图] [核心代码] 会员管理系统(strutshibernatespring) └── Co ...

  5. 疯狂讲义java 李刚_推荐:疯狂java讲义--李刚著作(4)

    >Java资料百度网盘 链接:https://pan.baidu.com/s/1IzjeTfM2SE5Ts9kRvS3huw 提取码:7wg9 >今日内容: >1.流程控制 > ...

  6. java忍者_找一款java忍者龟游戏,有吧友知道游戏名字吗?

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 <武动人物周刊>创刊号发布--被抢了主角光环的男人 他是妄图与主角抢光环的男人,明明是个反派,却有着不低的人气,他,就是林琅天! 据说< ...

  7. python开发环境推荐_推荐一款Python开发环境管理神器

    在 Python 开发中,有些情况下,我们可能面临在一台机器上同时安装多版本Python的需求.比如: 有多个Python项目,每个项目依赖不同的Python版本. 有一个Python项目,它需要同时 ...

  8. idea代码可以编译但是爆红_推荐一款 IDEA 生成代码神器,写代码再也不用加班了...

    作者:HeloWxl 链接:https://www.jianshu.com/p/e4192d7c6844 Easycode是idea的一个插件,可以直接对数据的表生成entity,controller ...

  9. 深受Java程序员欢迎的10款Java IDE

    概述:Java是全世界最受欢迎的一门程序语言,它被广泛的应用于企业项目.游戏设计.安卓应用程序等开发领域.以下的列表是整理的近些年来深受Java程序员欢迎的10款Java IDE. Java是全世界最 ...

最新文章

  1. php mysql insert数组_PHP – MySQL编写的INSERT数组语句
  2. sizebox模型下载_彻底弄懂CSS盒模型BoxModel
  3. Effective Java -- 思维导图
  4. Epic:把虚幻引擎推向所有游戏平台
  5. 【删库不跑路】—Linux系统自杀实验 rm -rf /* 及如何抢救和预防
  6. 二叉树前序遍历python输出_[宜配屋]听图阁 - Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例...
  7. macbook使用共享屏幕实现VNC远程控制
  8. 基于VGG19的图片分割网络
  9. 手机android怎么结束后台,如何关闭手机后台运行程序
  10. 使用MarkDown,编写html格式的项目帮助手册(含左侧目录树)
  11. luogu P2123 皇后游戏
  12. 好看的充电宝有哪些?好看的充电宝推荐
  13. JS中几种绑定事件的方式
  14. tomcat项目在Linux下生成文件路径错误问题
  15. 假信息正在伤害孩子!作为信息时代的原住民,孩子如何辨别信息真伪?
  16. python爬虫学习:selenium(2)
  17. Android中SurfaceView使用详解
  18. idea快速删除空行
  19. 碰到CTS问题我该如何处理?
  20. Word中的空白页,怎么也删不掉?如何操作?

热门文章

  1. 第12期【江山代有才人出】5月刊
  2. python使用matplotlib模块画饼图时出现方格乱码
  3. stream对象转map
  4. MyEclipse配置Maven
  5. Java数据类型(兔C内功残篇)
  6. 领域驱动设计在前端中的应用
  7. Qt中的信号与槽机制解析
  8. php time 毫秒_PHP microtime()函数获取毫秒时间戳
  9. 计算机视觉:从数据量、数据质量、数据复杂度、数据隐私介绍图片数据处理难度
  10. redis 主从配置(1主2从加哨兵)