Mapstruct中类型的映射规则(二)_mapstruct 父类属性-程序员宅基地

技术标签: mapstruct  

        上一篇文章主要讲解了Mapstruct的介绍、Mapstruct的优缺点、MapStruct简单的映射示例以及和常见的映射工具做了性能上的对比。

        这篇文章主要讲解Mapstruct的属性之间的映射规则,只有了解了这些规则之后,你才能更好地理解mapstruct如何做映射,才能按照你的想法进行对象属性之间的映射。

在属性映射方面,mapstruct相比于其他映射工具有:

  • 可以进行不同属性名之间的映射,使用@Mapping注解的source和target
  • 可以使用表达式进行设置,使用@Mapping注解中的expression
  • 可以设置默认值,使用@Mapping注解中的constant,
  • 可以进行不同类型之间的映射,并且更加安全
  • 可以进行自定义映射方法

01

mapstruct的映射文档


1.可以参考mapstruct的doc的第五章了解完整的映射规则:这里

02

mapstruct的映射控制说明


2.1 映射控制的作用:

        在源对象目标对象之间进行映射时,哪种映射方法将被考虑。即:只有在有这个控制条件下,才会执行对应的映射方法。

        所有属性之间的复制或者拷贝都将是依据以下映射控制来完成的。只有更好地理解这四种映射控制,才能按照我们的想法进行对象之间的拷贝。

2.2 映射控制的种类:

mapstruct一共有四种映射控制

  • MappingControl.Use.DIRECT

        直接映射:直接将源属性赋值到目标属性上,如果是对象的话,就是指针复制,也就是浅拷贝。这种方法有一个前提:目标属性是源属性的父类或者相同类型

  • MappingControl.Use.BUILT_IN_CONVERSION

        内置映射:将使用mapstruct内置的映射方法。有哪些内置映射方法:具体可参考文档第五章中第一节中。例如:日期和字符串、原生类型和对应的包装对象、字符串和枚举类型等等。

  • MappingControl.Use.MAPPING_METHOD

        映射方法:包含两类:可以是自定义的映射方法,也可以是系统自动生成的映射方法。注意:自定义映射方法比系统自动生成的映射方法的优先级更高。例如:深度拷贝就是使用这个映射控制。系统自动生成的映射方法:一般都是对象之间的映射。

  • MappingControl.Use.COMPLEX_MAPPING

        复合映射:是一种结合BUILT_IN_CONVERSIONMAPPING_METHOD的映射控制来完成对象属性之间的拷贝。当进行COMPLEX_MAPPING时:一共有三种形态:

                a) target = method1( method2( source ) )

                b) target = method( conversion( source ) )

                c) target = conversion( method( source ) )

        其中method开头表示:自定义映射方法,conversion开头的表示:内置映射方法

        注意:复合映射中只能进行两次转化,不能多于两次,也不能少于两次。这三种形态不含两个内置映射方法来完成属性之间的拷贝。即:至少有一个自定义的映射方法的存在。

        注意:在没有某一中映射控制时,就不会采用这种方式完成属性映射,例如:没有MAPPING_METHOD就会不采用自定义的映射方法完成属性映射。

2.3 映射控组组合

        四种映射控制可以组合使用,即同时使用两个及以上的映射控制,从而在这些映射控制中选择一种方式使用。那这就涉及到映射控制之间优先级

2.4 mapstruct内置的映射控制组合

        在mapstruct中一共有三种内置的映射控制组合,以及自定义的映射组合。

  • @MappingControl注解类

            该注解使用了全部的映射控制:DIRECT、MAPPING_METHOD、 BUILT_IN_CONVERSION、COMPLEX_MAPPING。这也这是:默认使用映射控制。
  • @DeepClone注解类

           该注解只使用了:MAPPING_METHOD映射,当我们使用这个注解时,将不会采用其他的三种方式来完成属性之间的拷贝。
  • @NoComplexMapping注解类

           改注解使用了三种:DIRECT、MAPPING_METHOD、 BUILT_IN_CONVERSION,即将不会使用复合映射方法完成属性之间的拷贝
  • 自定义的映射组合

                我们可以声明一个注解类,自定义只是用哪些映射控制,例如:只使用内置的映射方法

package com.moxiao.mapstruct.annotation;

import org.mapstruct.control.MappingControl;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @author moxiao
 */
@Retention(value = RetentionPolicy.CLASS)
@MappingControl( MappingControl.Use.BUILT_IN_CONVERSION )
public @interface BuildInConversionControl {
}

2.5 映射控制的使用

        mapstruct默认使用的是:@MappingControl注解类,即四种映射控制

        我们可以在@Mapper注解中的mappingControl属性中设置,他的属性值是一个注解类。在@Mapper注解中设置这个属性:将会应用到这个类中的所有方法的属性之间的拷贝

        我们也可以在@Mapping注解中的mappingControl属性中设置。在@Mapping注解中设置这个属性:将只会应用到指定属性上。

        @Mapping的优先级比@Mapper的高,即优先采用@Mapping,如果没有才会使用@Mapper进行属性之间的映射。

03

mapstruct的映射控制优先级


        在有多个映射控制使用时,例如在使用:@MappingControl注解,mapstruct将会如何进行属性之间的映射。这里就涉及到四种映射控制之间的优先级。

3.1 四种映射控制的优先级,从高到低

  1. 在有MAPPING_METHOD控制下,并且包含有一个自定义的映射方法。这个自定义方法必须满足:输入参数源属性父级或者相同类型,这个映射方法的返回值目标属性的子类或相同类型。必须都满足这两个条件:这样在进行属性拷贝时,才会使用自定义的映射方法。不然则跳过。
  2. 在有DIRECT控制下,并且目标属性必须是源属性的父类或者相同类型。功能:直接将源属性的指针拷贝到目标属性下。必须满足这两个条件,才会使用指针拷贝,不然则跳过。
  3. 如果有BUILT_IN_CONVERSION的控制下,在源属性和目标属性之间拷贝时,mapstruct就会根据源属性类型和目标属性类型找到对应的内置映射方法使用,没有找到,就跳过。
  4. 如果有COMPLEX_MAPPING控制下,它又划分成三种类型:

                 如果存在两个自定义的方法,可以将源属性转化为目标属性,即: target = method1( method2( source ) ),优先使用。

                如果有一个自定义方法,一个内置映射方法,可以将源属性转化为目标属性,就会使用其中一个target = method( conversion( source ) )或者target = conversion( method( source ) )。

     5. 如果有MAPPING_METHOD控制下,将自动生成一个映射方法,完成属性之间的映射。必须对象,属性不能是:8中基本类型和对应的包装类以及String

3.2 注意事项:

        如果源属性是8种基本类型或者包装体,而目标属性是对应的类型(不能是不对应的),则会直接拷贝。就算有就不会走上面的优先级。例如:源属性为long,目标属性为Long,不管配置的什么,都会直接赋值。

04

mapstruct的映射控制的示例


1.源对象

public class MappingOrder {

    private String name;

    private Integer age;

    private LocalDateTime birthTime;

    private MapperOrderChild mapperOrderChild;

    private MappingOrderChildChild mapperOrderChild2;
    //省略其set/get方法
 }

2.目标对象

public class MappingOrderVO {

    private String name;

    private Number age;

    private String birthString;

    private MapperOrderChild mapperOrderChild;

    private MapperOrderChild2 mapperOrderChild2;
 }

3.映射类(必须自己单独定义一个,mapstruct必须提供一个mapper接口或者抽象类)

package com.moxiao.mapstruct.mapper;

import com.moxiao.mapstruct.entity.MappingOrder;
import com.moxiao.mapstruct.entity.MappingOrderVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.control.DeepClone;

/**
 * @author moxiao
 */
@Mapper
public interface MapperOrderMapper {

    @Mapping(source = "birthTime", target = "birthString", dateFormat = "yyyy-MM-dd HH:mm:ss")
    //这一行进行深度拷贝,注意最后一个参数很重要:mappingControl = DeepClone.class
    @Mapping(source = "mapperOrderChild2", target = "mapperOrderChild2", mappingControl = DeepClone.class)
    MappingOrderVO entityToVo(MappingOrder mappingOrder);
    
}

4.mapstruct自动生成的文件

public class MapperOrderMapperImpl implements MapperOrderMapper {

    @Override
    public MappingOrderVO entityToVo(MappingOrder mappingOrder) {
        if ( mappingOrder == null ) {
            return null;
        }

        MappingOrderVO mappingOrderVO = new MappingOrderVO();
        
        //第一个属性,因为没有自定义方法从LocalDateTime转化为String,将跳过,
        //不能直接映射,优先级第二条
        //mapstruct有一个从LocalDateTime转化为String的内置方法,从而满足第三条映射
        if ( mappingOrder.getBirthTime() != null ) {
            mappingOrderVO.setBirthString( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ).format( mappingOrder.getBirthTime() ) );
        }
        
        //我们在方法中使用DEEPCLONE,即MAPPING_METHOD控制
        //所以只有第一条优先级和第五条优先级,满足。因为没有自定义映射方法,从而跳过
        //所以Mapstruct使用第五条,自动生成一个映射方法
        mappingOrderVO.setMapperOrderChild2( mappingOrderChildChildToMapperOrderChild2( mappingOrder.getMapperOrderChild2() ) );
        //DIRECT优先级获胜
        mappingOrderVO.setName( mappingOrder.getName() );
        //DIRECT优先级获胜
        mappingOrderVO.setAge( mappingOrder.getAge() );
        //DIRECT优先级获胜
        mappingOrderVO.setMapperOrderChild( mappingOrder.getMapperOrderChild() );
        return mappingOrderVO;
    }

    protected MapperOrderChild2 mappingOrderChildChildToMapperOrderChild2(MappingOrderChildChild mappingOrderChildChild) {
        if ( mappingOrderChildChild == null ) {
            return null;
        }
        MapperOrderChild2 mapperOrderChild2 = new MapperOrderChild2();

        mapperOrderChild2.setFirstName( mappingOrderChildChild.getFirstName() );
        mapperOrderChild2.setSecondName( mappingOrderChildChild.getSecondName() );

        return mapperOrderChild2;
    }
}

结论:最终生成的Java映射代码,会根据01章节中第5条的映射优先级,进行属性之间的映射。

更多精彩内容:请关注公众号:


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/cw_hello1/article/details/120193123

智能推荐

874计算机科学基础综合,2018年四川大学874计算机科学专业基础综合之计算机操作系统考研仿真模拟五套题...-程序员宅基地

文章浏览阅读1.1k次。一、选择题1. 串行接口是指( )。A. 接口与系统总线之间串行传送,接口与I/0设备之间串行传送B. 接口与系统总线之间串行传送,接口与1/0设备之间并行传送C. 接口与系统总线之间并行传送,接口与I/0设备之间串行传送D. 接口与系统总线之间并行传送,接口与I/0设备之间并行传送【答案】C2. 最容易造成很多小碎片的可变分区分配算法是( )。A. 首次适应算法B. 最佳适应算法..._874 计算机科学专业基础综合题型

XShell连接失败:Could not connect to '192.168.191.128' (port 22): Connection failed._could not connect to '192.168.17.128' (port 22): c-程序员宅基地

文章浏览阅读9.7k次,点赞5次,收藏15次。连接xshell失败,报错如下图,怎么解决呢。1、通过ps -e|grep ssh命令判断是否安装ssh服务2、如果只有客户端安装了,服务器没有安装,则需要安装ssh服务器,命令:apt-get install openssh-server3、安装成功之后,启动ssh服务,命令:/etc/init.d/ssh start4、通过ps -e|grep ssh命令再次判断是否正确启动..._could not connect to '192.168.17.128' (port 22): connection failed.

杰理之KeyPage【篇】_杰理 空白芯片 烧入key文件-程序员宅基地

文章浏览阅读209次。00000000_杰理 空白芯片 烧入key文件

一文读懂ChatGPT,满足你对chatGPT的好奇心_引发对chatgpt兴趣的表述-程序员宅基地

文章浏览阅读475次。2023年初,“ChatGPT”一词在社交媒体上引起了热议,人们纷纷探讨它的本质和对社会的影响。就连央视新闻也对此进行了报道。作为新传专业的前沿人士,我们当然不能忽视这一热点。本文将全面解析ChatGPT,打开“技术黑箱”,探讨它对新闻与传播领域的影响。_引发对chatgpt兴趣的表述

中文字符频率统计python_用Python数据分析方法进行汉字声调频率统计分析-程序员宅基地

文章浏览阅读259次。用Python数据分析方法进行汉字声调频率统计分析木合塔尔·沙地克;布合力齐姑丽·瓦斯力【期刊名称】《电脑知识与技术》【年(卷),期】2017(013)035【摘要】该文首先用Python程序,自动获取基本汉字字符集中的所有汉字,然后用汉字拼音转换工具pypinyin把所有汉字转换成拼音,最后根据所有汉字的拼音声调,统计并可视化拼音声调的占比.【总页数】2页(13-14)【关键词】数据分析;数据可..._汉字声调频率统计

linux输出信息调试信息重定向-程序员宅基地

文章浏览阅读64次。最近在做一个android系统移植的项目,所使用的开发板com1是调试串口,就是说会有uboot和kernel的调试信息打印在com1上(ttySAC0)。因为后期要使用ttySAC0作为上层应用通信串口,所以要把所有的调试信息都给去掉。参考网上的几篇文章,自己做了如下修改,终于把调试信息重定向到ttySAC1上了,在这做下记录。参考文章有:http://blog.csdn.net/longt..._嵌入式rootfs 输出重定向到/dev/console

随便推点

uniapp 引入iconfont图标库彩色symbol教程_uniapp symbol图标-程序员宅基地

文章浏览阅读1.2k次,点赞4次,收藏12次。1,先去iconfont登录,然后选择图标加入购物车 2,点击又上角车车添加进入项目我的项目中就会出现选择的图标 3,点击下载至本地,然后解压文件夹,然后切换到uniapp打开终端运行注:要保证自己电脑有安装node(没有安装node可以去官网下载Node.js 中文网)npm i -g iconfont-tools(mac用户失败的话在前面加个sudo,password就是自己的开机密码吧)4,终端切换到上面解压的文件夹里面,运行iconfont-tools 这些可以默认也可以自己命名(我是自己命名的_uniapp symbol图标

C、C++ 对于char*和char[]的理解_c++ char*-程序员宅基地

文章浏览阅读1.2w次,点赞25次,收藏192次。char*和char[]都是指针,指向第一个字符所在的地址,但char*是常量的指针,char[]是指针的常量_c++ char*

Sublime Text2 使用教程-程序员宅基地

文章浏览阅读930次。代码编辑器或者文本编辑器,对于程序员来说,就像剑与战士一样,谁都想拥有一把可以随心驾驭且锋利无比的宝剑,而每一位程序员,同样会去追求最适合自己的强大、灵活的编辑器,相信你和我一样,都不会例外。我用过的编辑器不少,真不少~ 但却没有哪款让我特别心仪的,直到我遇到了 Sublime Text 2 !如果说“神器”是我能给予一款软件最高的评价,那么我很乐意为它封上这么一个称号。它小巧绿色且速度非

对10个整数进行按照从小到大的顺序排序用选择法和冒泡排序_对十个数进行大小排序java-程序员宅基地

文章浏览阅读4.1k次。一、选择法这是每一个数出来跟后面所有的进行比较。2.冒泡排序法,是两个相邻的进行对比。_对十个数进行大小排序java

物联网开发笔记——使用网络调试助手连接阿里云物联网平台(基于MQTT协议)_网络调试助手连接阿里云连不上-程序员宅基地

文章浏览阅读2.9k次。物联网开发笔记——使用网络调试助手连接阿里云物联网平台(基于MQTT协议)其实作者本意是使用4G模块来实现与阿里云物联网平台的连接过程,但是由于自己用的4G模块自身的限制,使得阿里云连接总是无法建立,已经联系客服返厂检修了,于是我在此使用网络调试助手来演示如何与阿里云物联网平台建立连接。一.准备工作1.MQTT协议说明文档(3.1.1版本)2.网络调试助手(可使用域名与服务器建立连接)PS:与阿里云建立连解释,最好使用域名来完成连接过程,而不是使用IP号。这里我跟阿里云的售后工程师咨询过,表示对应_网络调试助手连接阿里云连不上

<<<零基础C++速成>>>_无c语言基础c++期末速成-程序员宅基地

文章浏览阅读544次,点赞5次,收藏6次。运算符与表达式任何高级程序设计语言中,表达式都是最基本的组成部分,可以说C++中的大部分语句都是由表达式构成的。_无c语言基础c++期末速成