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

智能推荐

【Unity3d】百度AI人脸识别实例——描绘人脸特征点_unity 如何判断texture2d 是否黑色-程序员宅基地

文章浏览阅读5.1k次。上一篇文章介绍了在Unity3d中使用百度AI人脸识别功能,本文将用一个描绘人脸特征点的程序来讲解对SDK的调用。首先我们需要搭建一个简单的场景:一个开始测试的按钮,一张供测试和绘制特征点的图片,和显示debug信息的文字框: 然后新建一个叫FaceDetector的脚本,定义变量:public Text debugInfo; // 显示d_unity 如何判断texture2d 是否黑色

filehelper java_Caused by: java.nio.file.FileSystemException: ..... : 打开的文件过多-程序员宅基地

文章浏览阅读1k次。在高并发处理图片的时候出现这个错误。18:29:37.993 [pool-9-thread-8] ERROR cn.bywin.cbvsp.service.ImageSevice - 切图出错!!javax.imageio.IIOException: Can't create cache file!at javax.imageio.ImageIO.createImageOutputStream(I..._filesystemexception 打开的文件过多

个人书签 备份(上)_nvdla compiler-程序员宅基地

文章浏览阅读176次。BookmarksBookmarks 书签栏 姿态识别 2D3D姿态识别 IT网站-首页 TVM (99+ 封私信) TVM源码解析 - 搜索结果 - 知乎 TVM整体结构,TVM代码的基本构成 - 综合技术交流 - 电子技术论坛 - 广受欢迎的专业电子论坛! 源码研习 — T..._nvdla compiler

通俗易懂QPS、TPS、PV、UV、GMV、IP、RPS的概念解释_qpsvr-程序员宅基地

文章浏览阅读1.2w次。前言关于 QPS、TPS、PV、UV、GMV、IP、RPS 这些词语,看起来好像挺专业。但实际上,我认为是这是每个程序员必懂的知识点了,你可以搞不懂它们怎么计算的,但是你最少要了解它们分别代表什么意思。QPSQueries Per Second,每秒查询数。每秒能够响应的查询次数。QPS 是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,在因特网上,作为域名系统服务器的机器的..._qpsvr

ORA-00132syntax error or unresolved network name-程序员宅基地

文章浏览阅读2.3k次。在DOS下SQL> startup;ORA-00119: invalid specification for system parameter LOCAL_LISTENERORA-00132: syntax error or unresolved network name 'LISTENER_ORCL'解决方法:1.SQL> create pfile from spf..._ora-00132

C/C++编程笔记:quick_exit函数和ctime函数-程序员宅基地

文章浏览阅读650次。今天给大家分享学习两个函数:quick_exit函数和ctime函数。quick_exitquick_exit()函数是在定义STDLIB头文件。quick_exit()函数用于正常终止进程而不完全清除资源。如果val为零或EXIT_SUCCESS,则表明程序成功终止。如果该值不为零或EXIT_FAILURE,则表明该程序未成功终止。这些函数以那里调用的相反顺序被调用。句法:void quick_exit(int val);参数:此方法采用单个参数val,它是表示程序退出状态_quick_exit

随便推点

SQL 数据库 学习 024 查询-07 order by 的用法 --- 以某个字段排序_sql使数据按字段排序-程序员宅基地

文章浏览阅读1.3w次。我的电脑系统:Windows 10 64位SQL Server 软件版本: SQL Server 2014 Express 本篇博客里面使用了 scott 库,如何你现在还没有添加这个库到你的服务器里面,请在查看本篇博客前,访问这篇博文来在你的服务器里面附加scott库。order by — 以某个字段排序例子:select * from emp order by sal; --默认_sql使数据按字段排序

解决360浏览器不支持IDM下载的问题_360下载失败怎么用idm安装-程序员宅基地

文章浏览阅读4.5w次,点赞2次,收藏2次。像360安全浏览器和360极速浏览器的设置中,下载工具都只能选择自己。不能选择IDM下载。 如图: 如何让它们支持IDM下载呢? 在IDM的文件夹中,可以看到一个扩展文件,将它拖动的浏览器中进行安装即可。 如图: 然后打开IDM,可以看到360安全/技术浏览器了。如果没有手动添加浏览器。 然后就可以用IDM下载了。..._360下载失败怎么用idm安装

scala方法中的变量_Scala中的变量-程序员宅基地

文章浏览阅读405次。scala方法中的变量 Scala变量 (Scala variables)A variable is named a reference to a memory location. The location stores the data that is used by the program. 变量被称为对存储位置的引用。 该位置存储程序使用的数据。 Based on the data t..._scala 方法 变量

研报回测:华泰期货《CTA 量化策略因子系列(二):动量因子》_华泰期货cta量化策略因子-程序员宅基地

文章浏览阅读5.9k次,点赞4次,收藏17次。我们都知道做投资最难的地方在于投资逻辑是否准确,如何验证逻辑的准确性是一门工程,从工科思维考虑就是通过历史数据做回测验证,以检验投资逻辑在历史中的表现,从而衡量投资逻辑的有效性。本文从华泰期货研报《20170925-华泰期货-CTA量化策略因子系列(二):动量因子》入手,去论证动量因子是否有效。一、准备巧妇难为无米之炊,首先我们需要准备成交量、动量和收益率的数据,收益率我们采用每天的涨跌幅来计..._华泰期货cta量化策略因子

软件项目全套通用资料(规格说明书~详细设计~测试计划~验收报告)-程序员宅基地

文章浏览阅读503次,点赞9次,收藏7次。在软件开发过程中,文档资料是非常关键的一部分,它们帮助团队成员理解项目需求、设计、实施、测试、验收等各个环节,确保项目的顺利进行。以下是针对您提到的各个阶段的文档资料概述:

11.20 开课二个月零十六天(流程管理)-程序员宅基地

文章浏览阅读64次。1.建立一个添加页面,在这个页面上可以选择增加节点。add.php<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3....

推荐文章

热门文章

相关标签