看了MapStruct使用入门后,相信小伙伴已经熟悉了MapStruct这款工具的初步使用。

今天接着进阶一下,来达到更深入了解和使用的目的。

我们已经知道基本类和包装类,以及String类这些,我们都不用做其他辅助操作,MapStruct就自动帮我们处理好了。

但是日常项目开发中,不可能总是简单对象属性直接的转换。有Map,List,其他对象,以及枚举类等都可能出现在我们的对象属性中,甚至你要转换的对象之间属性类型都不一样(当然不是基础类和包装类的区别哈),比如String和LocalDateTime之间的对象值copy,该如何处理?今天我们就一步步的探索,来解决这些问题。请往下看!

No1. A对象中有属性为List类型,B对象中是String[]类型

这种情况下,如果两个对象的属性名一样的话,MapStruct可以帮我们自动处理。

@Data
public class UserBO {private String name;private Integer id;private String address;private String email;private Integer age;private String gender;private List<String> hobbies;
}
@Data
public class UserDO {private String name;private Integer id;private String address;private String email;private Integer age;private String gender;private String[] hobbies;
}
@Mapper
public interface MapStructConvertUtil {MapStructConvertUtil INSTANCE = Mappers.getMapper(MapStructConvertUtil.class);UserDO convert(UserBO userBO);
}
public class MapStructTest {public static void main(String[] args) {UserBO userBO = new UserBO();userBO.setName("AwesomeJokerWang");userBO.setAddress("TJ");userBO.setAge(25);userBO.setEmail("123456789@163.com");userBO.setGender("Man");userBO.setId(1);userBO.setHobbies(new ArrayList<String>(){{add("唱");add("跳");add("Rap");}});System.out.println(userDO);}
}

执行结果自然是没得问题,hobbies的值完美copy。

看看生成后impl文件,是不是感觉有点牛批。别着急,厉害还没完呢。

@Generated(value = "org.mapstruct.ap.MappingProcessor",date = "2021-06-18T18:00:46+0800",comments = "version: 1.4.2.Final, compiler: javac, environment: Java 1.8.0_292 (Amazon.com Inc.)"
)
public class MapStructConvertUtilImpl implements MapStructConvertUtil {@Overridepublic UserDO convert(UserBO userBO) {if ( userBO == null ) {return null;}UserDO userDO = new UserDO();userDO.setName( userBO.getName() );userDO.setId( userBO.getId() );userDO.setAddress( userBO.getAddress() );userDO.setEmail( userBO.getEmail() );userDO.setAge( userBO.getAge() );userDO.setGender( userBO.getGender() );userDO.setHobbies( stringListToStringArray( userBO.getHobbies() ) );return userDO;}protected String[] stringListToStringArray(List<String> list) {if ( list == null ) {return null;}String[] stringTmp = new String[list.size()];int i = 0;for ( String string : list ) {stringTmp[i] = string;i++;}return stringTmp;}
}

这是两个对象的属性名一致的情况下,我们甚至都不要对我们的接口做任何改动就可以达到效果。但是,如果两个对象的属性名不一样怎么办呢。这下你再厉害也找不到了哇,我们试一下。

在UserDO上做一点点改动,让它和UserBO中的hobbies属性的属性名不一样。其他都不变,我们测试一下结果。

@Data
public class UserDO {private String name;private Integer id;private String address;private String email;private Integer age;private String gender;private String[] d_hobbies;
}

果不其然,终于为null了哇,和我们猜想的一样,而且接口的实现类中也没有了hobbies属性的赋值操作。

@Generated(value = "org.mapstruct.ap.MappingProcessor",date = "2021-06-18T18:09:04+0800",comments = "version: 1.4.2.Final, compiler: javac, environment: Java 1.8.0_292 (Amazon.com Inc.)"
)
public class MapStructConvertUtilImpl implements MapStructConvertUtil {@Overridepublic UserDO convert(UserBO userBO) {if ( userBO == null ) {return null;}UserDO userDO = new UserDO();userDO.setName( userBO.getName() );userDO.setId( userBO.getId() );userDO.setAddress( userBO.getAddress() );userDO.setEmail( userBO.getEmail() );userDO.setAge( userBO.getAge() );userDO.setGender( userBO.getGender() );return userDO;}
}

解决这个问题,需要引出MapStruct的关键注解@Mapping。showcode。

只需要在转换接口上加入@Mapping注解,指明source的值和target的值即可做匹配,这么一来,MapStruct就可以匹配到不同名字的属性了。@Mappings是可以包裹多组@Mapping对象的。在@Mapping映射较多的时候可以用@Mappings包裹,也可以累加多个@Mapping,这个没什么影响。

@Mapper
public interface MapStructConvertUtil {MapStructConvertUtil INSTANCE = Mappers.getMapper(MapStructConvertUtil.class);@Mappings({@Mapping(source = "hobbies", target = "d_hobbies")})UserDO convert(UserBO userBO);
}

看结果。hobbies的值可以正确copy,而且接口的实现类也和上一个一样。

那么,List和List,Map和HashMap等类型的复制也是一样,只要两个对象的属性名一样,就不用使用@Mapping注解来手动匹配,即使copy对象之间的属性类型是子父关系,也可以直接复制值,具体实现我就不一一列举了,有兴趣的小伙伴可以自己试试。

再来看一下第二种情况,如果上例中的B对象中hobbies的属性是String类型,不使用@Mapping,MapStruct能自动帮我们匹配么?showcode

B对象的hobbies属性类型改为String,接口去掉@Mapping注解。

@Data
public class UserDO {private String name;private Integer id;private String address;private String email;private Integer age;private String gender;private String hobbies;
}
@Mapper
public interface MapStructConvertUtil {MapStructConvertUtil INSTANCE = Mappers.getMapper(MapStructConvertUtil.class);UserDO convert(UserBO userBO);
}

测试结果:build的时候就报错了,List和String直接映射不了。

java: Can't map property "List<String> hobbies" to "String hobbies". Consider to declare/implement a mapping method: "String map(List<String> value)".

那如果把@Mapping注解加上,手动匹配后,能正确build么?可能此时有小伙伴就想到了,那A和B的对象中属性名不是一样么,你不是上面说了属性名一样的话,不用加@Mapping来手动映射么。没错,此时即使你用@Mapping注解匹配了,也还是报上面的错。因为错误的原因并不是两个对象找不到匹配,而是List和String cat not map(不能映射)。

看一下加了@Mapping的结果

@Mapper
public interface MapStructConvertUtil {MapStructConvertUtil INSTANCE = Mappers.getMapper(MapStructConvertUtil.class);@Mappings({@Mapping(source = "hobbies", target = "hobbies")})UserDO convert(UserBO userBO);
}
java: Can't map property "List<String> hobbies" to "String hobbies". Consider to declare/implement a mapping method: "String map(List<String> value)".

那么,怎么解决?@Mapping注解里除了source和target,还有一个expression可以用。

@Repeatable(Mappings.class)
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Mapping {String target();String source() default "";String dateFormat() default "";String numberFormat() default "";String constant() default "";String expression() default "";String defaultExpression() default "";boolean ignore() default false;Class<? extends Annotation>[] qualifiedBy() default {};String[] qualifiedByName() default {};Class<?> resultType() default void.class;String[] dependsOn() default {};String defaultValue() default "";NullValueCheckStrategy nullValueCheckStrategy() default NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;NullValuePropertyMappingStrategy nullValuePropertyMappingStrategy() default NullValuePropertyMappingStrategy.SET_TO_NULL;Class<? extends Annotation> mappingControl() default MappingControl.class;
}

我们改一下接口里的@Mapping写法,用expression方法处理List和String。这里说明一下:使用expression方法后,就不用写source了,因为表达式参数里需要写source参数,不用重复写。

@Mapper
public interface MapStructConvertUtil {MapStructConvertUtil INSTANCE = Mappers.getMapper(MapStructConvertUtil.class);@Mappings({@Mapping(target = "hobbies", expression = "java(userBO.getHobbies().get(0))")})UserDO convert(UserBO userBO);
}

再测试看一下结果。成功赋值,我们用userBO.getHobbies().get(arg)来获取需要匹配的参数

其实,介绍到这,小伙伴们也基本也就知道了,对象属性类型或名字不一样时,用@Mapping注解处理就可以,遇到从集合中取某一个值和单个值匹配时,用expression表达式处理就行。expression的写法也很简单,Java方法里写你的逻辑处理就行,就跟写Java代码一样。

expression = “java(处理逻辑)” 

后面再简单看一下对象中有枚举,以及String和LocalDateTime之间的代码示例。因为核心的处理就是expression的使用

No2. A对象中有属性为Enum类型,B对象中是基本类型或包装类型

@Data
public class UserBO {private String name;private Integer id;private String address;private String email;private Integer age;private String gender;private List<String> hobbies;private SkillEnums role;
}
@Data
public class UserDO {private String name;private Integer id;private String address;private String email;private Integer age;private String gender;private String hobbies;private String d_role;
}

SkillEnums对象

@Getter
@AllArgsConstructor
public enum SkillEnums {JAVA("java","25年了"),PHP("php", "号称全世界最好的语言"),C_Sharp("c#", "微软小宝贝儿"),C("c","计算机原理");private final String code;private final String msg;public static String getSkill(String code){SkillEnums[] values = SkillEnums.values();for (int i = 0; i < values.length; i++) {if (code.equals(values[i].getCode())) {return values[i].getCode();}}return "";}
}

对象名一样的话不用@Mapping,不一样的话加个@Mapping手动做匹配,经测试可以成功赋值。

我们主要看一MapStruct帮我们生成的接口实现类。不得不说MapStruct太厉害了。

@Generated(value = "org.mapstruct.ap.MappingProcessor",date = "2021-06-19T23:33:43+0800",comments = "version: 1.4.2.Final, compiler: javac, environment: Java 1.8.0_292 (Amazon.com Inc.)"
)
public class MapStructConvertUtilImpl implements MapStructConvertUtil {@Overridepublic UserDO convert(UserBO userBO) {if ( userBO == null ) {return null;}UserDO userDO = new UserDO();userDO.setName( userBO.getName() );userDO.setId( userBO.getId() );userDO.setAddress( userBO.getAddress() );userDO.setEmail( userBO.getEmail() );userDO.setAge( userBO.getAge() );userDO.setGender( userBO.getGender() );if ( userBO.getRole() != null ) {userDO.setRole( userBO.getRole().name() );}userDO.setHobbies( userBO.getHobbies().get(0) );return userDO;}
}

No3. A对象中有属性为String类型,B对象中是LocalDate或LocalDateTime等类型

@Data
public class UserBO {private String name;private Integer id;private String address;private String email;private Integer age;private String gender;private List<String> hobbies;private SkillEnums role;private String birthday;
}
@Data
public class UserDO {private String name;private Integer id;private String address;private String email;private Integer age;private String gender;private String hobbies;private String role;private LocalDate birthday;
}

修改一下接口,增加expression表达式处理String和LocalDate的转换。DateUtils.convertDate是我写的一个工具类。

@Mapper
public interface MapStructConvertUtil {MapStructConvertUtil INSTANCE = Mappers.getMapper(MapStructConvertUtil.class);@Mappings({@Mapping(target = "hobbies", expression = "java(userBO.getHobbies().get(0))"),@Mapping(target = "birthday", expression = "java(DateUtils.convertDate(userBO.getBirthday()))")})UserDO convert(UserBO userBO);
}

DateUtils.class

public class DateUtils {public static LocalDate convertDate(String strDate) {DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");return LocalDate.parse(strDate, formatter);}
}

OK,介绍到这,我想小伙伴就已经对MapStruct的使用有了更进一步的了解。

不得不说MapStruct这个小工具很强大,很简单,也很好用。后续有想要深入了解的小伙伴可以看看源码,核心的Mappers方法实现以及其他的注解。

好了,MapStruct的使用基本就介绍完了,项目中可以考虑使用这款强大而工具来实现对象之间的转换,而不用再写那么多繁琐的set转换了。

最后,如果觉得写的对您有用,希望您留下您的素质三连,非常感谢。

MapStruct进阶使用<二>相关推荐

  1. Linux 进阶笔记(二)

    这几篇博文主要记录博主的Linux 学习之路,用作以后回顾和参考.大家可以选择略过也可以作参考. (一)Linux 初步笔记 (二)Linux 进阶笔记(一) (三)Linux 进阶笔记(二) (四) ...

  2. Quartz.Net进阶之二:关于触发器的更多信息

    Quartz.Net进阶之二:关于触发器的更多信息 原文:Quartz.Net进阶之二:关于触发器的更多信息 与作业一样,触发器相对容易使用,但是在您可以充分利用Quartz.NET之前,确实需要了解 ...

  3. Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈)

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  4. Wireshark入门与进阶系列(二)

    摘自http://blog.csdn.net/howeverpf/article/details/40743705 Wireshark入门与进阶系列(二) "君子生非异也,善假于物也&quo ...

  5. JVM进阶(十二)——JAVA 可视化分析工具

    JVM进阶(十二)--JAVA 可视化分析工具   经过前几篇博文对堆内存以及垃圾收集机制的学习,相信小伙伴们已经建立了一套比较完整的理论体系!本篇博客就根据已有的理论知识,通过可视化工具来实践一番. ...

  6. mysql 进阶(二)

    ##mysql 进阶(二) ###多表联合查询 什么是多表联合查询 前面所讲的查询语句都是针对一个表的,但是在关系型数据库中,表与表之间是有联系的,所以在实际应用中,经常使用多表查询.多表查询就是同时 ...

  7. nas安装emby_威联通QNAP系统入门进阶 篇二:宅家新姿势—威联通NAS安装套件版Emby搭建家庭影音服务器...

    威联通QNAP系统入门&进阶 篇二:宅家新姿势-威联通NAS安装套件版Emby搭建家庭影音服务器 2020-02-04 19:38:54 123点赞 1466收藏 123评论 你是AMD Ye ...

  8. 失传万年的PS合成进阶宝典(二)

    合成中辅助工具 失传万年的PS合成进阶宝典(一):  失传万年的PS合成进阶宝典(一) 失传万年的PS合成进阶宝典(二):  失传万年的PS合成进阶宝典(二) 1: 使用自由变换制作路面文字效果 首先 ...

  9. Easyx进阶(二)

    Easyx进阶(二) --消息处理 消息缓冲区可以缓冲 63 个未处理的消息.每次获取消息时,将从消息缓冲区取出一个最早发生的消息.消息缓冲区满了之后,不再接收任何消息. 1. 消息以及分类 1.1 ...

  10. Java语言基础(Java自我进阶笔记二)

    Java语言基础(Java自我进阶笔记二) 一. 什么是Java 的主类结构? 1. #mermaid-svg-xWTL2A8kDyyRPexH .label{font-family:'trebuch ...

最新文章

  1. 进程创建函数fork()和vfork()
  2. windows mobile开发循序渐进(4)移动应用程序的数据存储之本地数据存储第二篇
  3. MyBatis+Spring MVC开发指南(一)
  4. 写接口给别人调用 推送数据到我们_我们写了一个超好用的抖音矩阵数据管理工具...
  5. 一文彻底搞懂静态库和动态库,显示链接和隐式链接
  6. Android 8.0(29)---Android 8.0 获取当前的activity
  7. BloomFilter, Count-Min Sketch算法
  8. Linux 怎么找回管理员密码?
  9. 嵌入式linux系统开发教程
  10. matlab生成范德蒙矩阵
  11. 红帽linux创建c文件,RedHat成功运行的第一个C程序全过程(适合新手)
  12. 【R语言】预测模型最合适阈值Cutoff选取及其它指标计算
  13. 【学习点滴】cpp遇到的一些疑问和积累
  14. 探究MySQL的索引结构选型
  15. C盘全面清理教程,彻底清理所有垃圾
  16. 分享110个PHP源码,总有一款适合您
  17. 1688获得店铺的所有商品教程
  18. R12_专题知识总结提炼-AP模块
  19. ubuntu GPIO口操作
  20. 开放银行潜在风险不可忽视,看F5如何应对?

热门文章

  1. 英特尔处理器能够用鸿蒙系统吗,华为下月发布新型电脑,非英特尔、AMD平台,或将搭载鸿蒙系统?...
  2. 涉及继承、接口、重写、重载等多个知识点的程序练习
  3. oracle 10046
  4. bert-embedding:如何得到BERT训练的词向量
  5. ZeroMQ|调库调参侠福利小鱼看到一个有意思的库~
  6. IntelliJ IDEA一个窗口打开多个项目
  7. 环境退化识别(长走廊)
  8. win服务器远程桌面怎么开,如何开启windowsserver 2008r2的远程桌面功能
  9. html5多张背景图播放,4张照片制作相册视频添加大于5分钟的背景音乐 画面停留在最后一张图片继续播放音乐...
  10. C语言——switch....case函数用法