『MapStruct』POJO 映射转换框架指南

news2024/11/23 7:04:58

前言

本文将会主要讲解后端开发中 VO、DTO、Entity 相互转化方式,并且针对其中比较成熟的框架 MapStruct 进行解读和教学

下载

问题

微服务架构下,服务拆分会产生 VO、DTO、Entity 三类 POJO

  • VO 用于前端接口参数传递,例如用于 http 接口接收请求参数。可以继承扩展 DTO,或者直接使用 DTO
  • DTO 用于 rpc 接口参数传递。单独定义,或者继承扩展其他 rpc 接口的 DTO
  • Entity(PO) 用于 orm 映射处理,与表结构对应,只在服务内部使用,不能对外

注:对于 POJO 的解释可以查看文章后面的 补充 章节

微服务架构面向不同场景的 POJO 定义,引入前端请求处理问题,也就是三者之间的转换

  • 请求:VO => DTO => Entity
  • 返回:Entity => DTO => VO

结构

Entity

@Data
@TableName(value = "orders", schema = "crazy1984")
public class Order {
    @TableId(type = IdType.AUTO)
    private int id;
    private String orderId;
    private String orderType;
    private int orderStatus;
    private Date createdAt;
    private Date updatedAt;
}

DTO

@Data
public class OrderDTO {
    private String orderId;
    private String orderType;
    private int orderStatus;
    private Date createdAt;
}

VO

@Data
public class OrderVO extends OrderDTO{
    private String orderTypeName;
    private String orderStatusName;
}

手动转换

我们可以使用最直接的方法,通过代码对 POJO 属性进行逐个拷贝

但是这样的方式太低效,给开发人员增加许多低效的重复劳动,也不易维护(比如新增字段时,所有相关处都要同步修改)

OrderDTO dto = new OrderDTO();
dto.setOrderId(entity.getOrderId());
dto.setOrderType(entity.getOrderType());
dto.setOrderStatus(entity.getOrderStatus());
dto.setCreatedAt(entity.getCreatedAt());

工具类转换

改进方法为,使用工具类进行同名属性的自动拷贝,例如使用 Spring 的 BeanUtils

OrderDTO dto = new OrderDTO();
BeanUtils.copyProperties(entity, dto);

这样可以减少大量工作,但是会带来如下不足:

  • 不支持属性名映射,属性名必须完全相同
  • 不支持自动类型转换,Spring 的 BeanUtils 要求源属性与目标属性的类型是相互 assignable 的
  • 性能损耗大,属性拷贝中的属性名匹配、类型检查、写权限检查都是动态判断,有性能损耗

除此之外,还有很多工具类,下图为各类工具类对比

拷贝工具使用效率
Spring BeanUtils使用方便,效率中等
Cglib BeanCopier使用方便,效率最高
Apache BeanUtils使用方便,效率低,原因为该工具做了很多校验,兼容,日志打印等,导致性能下降(阿里约束规范中禁止使用该工具
Apache PropertyUtils使用方便,效率低
Hutool BeanUtil使用方便,封装完善,效率较高

映射框架转换

目前有很多开源成熟的 mapping 框架:

  • Dozer - Usage (sourceforge.net)
  • Orika reference guide (orika-mapper.github.io)
  • ModelMapper - Simple, Intelligent, Object Mapping.
  • MapStruct – Java bean mappings, the easy way!
  • JMapper Framework (jmapper-framework.github.io)

以上框架均支持不同属性名的映射自动类型转换递归映射自定义对象属性

实现原理为基于反射机制,实现类属性的 get,set 调用,基于注解、配置,来实现不同属性名的映射和类型转化

框架的性能对比MapStructJMapper 性能较好。因为他们的映射过程是静态化的,所以实际性能和自己手写 get、set 一样

并且在开发过程中,可以通过检查生成的代码来确保映射转换没有错误,相比 ModelMapper 的黑盒实现更加可靠。对于 grpc 协议 protobuf 对象和 entity 的互相转换,也能很好的支持

下面我们将会详细讲解 MapStruct 框架的使用

MapStruct

简介

官方仓库:mapstruct/mapstruct: An annotation processor for generating type-safe bean mappers (github.com)

官方文档:MapStruct – Java bean mappings, the easy way!

MapStruct 是代码生成器,基于约定而不是配置,极大地简化 Java Bean 类型之间映射的实现

安装

官方文档:Installation – MapStruct

首先我们需要向 pom.xml 中添加如下内容

注:本文使用 Maven 为例,Gradle 用户可以参照上方的官方文档

...
<properties>
    <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
</properties>
...
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source> <!-- depending on your project -->
                <target>1.8</target> <!-- depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                    <!-- other annotation processors -->
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

示例

官方示例代码:mapstruct-examples/mapstruct-mapper-repo at main · mapstruct/mapstruct-examples (github.com)

我们可以直接单独下载上面对应 demo 的项目文件夹,具体方法参照文末 补充 章节

您也可以自行创建 Spring 项目,然后依次安装 MapStruct 后,创建如下文件

Car.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {

    private String make;
    private int numberOfSeats;
    private CarType type;
    
}

CarType.java

public enum CarType {
    SPORTS, OTHER;
}

CarDto.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {

    private String make;
    private int seatCount;
    private String type;

}

CarMapper.java

@Mapper
public interface CarMapper {
 
    CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
 
    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car);
}

TestMapperRepo.java

public class TestMapperRepo {
    @Test
    public void shouldMapCarToDto() {
        //given
        Car car = new Car("Morris", 5, CarType.SPORTS);

        //when
        CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);

        //then
        assertThat(carDto).isNotNull();
        assertThat(carDto.getMake()).isEqualTo("Morris");
        assertThat(carDto.getSeatCount()).isEqualTo(5);
        assertThat(carDto.getType()).isEqualTo("SPORTS");
    }
}

但是 MapStruct 默认是和 Lombok 冲突的,无法识别,会出现属性找不到错误,您可以将 Lombok 注释替换为对应代码或者是 pom.xml 替换如下

<properties>
    <org.projectlombok.version>1.18.16</org.projectlombok.version>
    <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
    <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${org.projectlombok.version}</version>
    </dependency>

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${org.projectlombok.version}</version>
                    </path>
                    <!-- This is needed when using Lombok 1.18.16 and above -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>${lombok-mapstruct-binding.version}</version>
                    </path>
                    <!-- Mapstruct should follow the lombok path(s) -->
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

我们需要确保 Lombok 最低版本为 1.18.16,同时 annotationProcessorPaths 中,mapstruct-processor 的配置要在 lombok 之后

补充

POJO

POJO vs Java Beans - GeeksforGeeks

简介

POJO(Plain Old Java Object) 字面翻译为 “纯洁老式的 Java 对象”,但是其更加通俗的名称为 “简单 Java 对象

内在含义不继承或不实现任何其它 Java 框架的类或接口,没有被其它框架侵入的 Java 对象

注:里面的类和接口仅指的是其它 Java 框架中的类和接口,而不是所有类和接口

相关链接:

  • POJO (martinfowler.com)
  • Plain old Java object - Wikipedia
转换对象

我们可以辅助理解 POJO 是中间对象

该中间对象可以根据不同情况转换为 PO、DTO、VO

1 .POJO 持久化之后 --> PO(Persistent Object)

2 .POJO 传输过程中 --> DTO(Data Transfer Object)

3 .POJO 用作表示层 --> VO(View Object)

4 .POJO 用作业务逻辑层 --> BO(Business Object)

注:BO 主要作用是把业务逻辑封装为对象这个对象可以包括一个或多个其它的对象,BO 通过调用 DAO 方法,结合 PO,VO 进行业务操作

GitHub 单独下载文件夹

单独下载文件不必多说,我们点击进入 GitHub 的文件之后右上角会有 raw 文件下载按钮,点击即可下载源文件

如果是下载文件夹的话,有以下方式

GitZip

下载谷歌插件 GitZip

链接:GitZip for github - Chrome 应用商店 (google.com)

安装之后我们双击对应的文件夹,然后点击右下角的下载按钮即可下载

image-20231104112214588

DownGit

网站地址:DownGit (minhaskamal.github.io)

参考链接

  • Quick Guide to MapStruct | Baeldung
  • POJO、PO、DTO、VO、BO ? EJB、EntityBean
  • 什么是JavaBean? - JYRoy - 博客园 (cnblogs.com)
  • JavaBean - 廖雪峰的官方网站 (liaoxuefeng.com)
  • 微服务中VO、DTO、Entity间的相互转换处理
  • Github | 如何在Github上只下载一个文件或文件夹

本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1185451.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

​【错误解决方案】ModuleNotFoundError: No module named ‘ahocorasick‘

1. 错误提示 ModuleNotFoundError: No module named ahocorasick&#xff0c;这意味着你试图导入一个名为 ahocorasick的模块&#xff0c;但Python找不到这个模块 2. 解决方案 安装缺失的模块: 如果你确定模块名称正确但仍然收到这个错误&#xff0c;那么可能是你没有安装这个…

基于springboot+vue的影城管理系统

影城管理系统 编号&#xff1a;springboot33 源码合集&#xff1a; www.yuque.com/mick-hanyi/javaweb 源码下载&#xff1a;博主私 目 录 摘 要 1 前 言 2 第1章 概述 2 1.1 研究背景 3 1.2 研究目的 3 1.3 研究内容 4 第二章 开发技术介绍 5 2.1相关技术 5 2.2 Java技术 6…

逆向-文心一言开发者控制台调试

一打开标准的无限debugger 往上一层可以发现是jsvmp&#xff0c;这样替换文件相对来说就不太好搞 根据测试如果卡在debugger就会跳转页面 但是放行debugger就可以正常使用 可以基本确定debugger前后存在计时程序 这个时候就可以考虑对apply做hook劫持无限debugger的函数&#…

每天都很煎熬,领导派的活太难,真的想跑路了

人在江湖身不由己&#xff0c;无论是领导的亲信还是团队的边缘&#xff0c;都可能遇到这种情况———不得不干一件特别难以推进的事情&#xff0c;茫然无措&#xff0c;不知如何推进。每天陷入焦虑和自我怀疑中…… 这种事情一般有一些共同特点。 结果和目标极其模糊。需要协…

企业防范数据安全的重要性与策略

随着信息技术的快速发展&#xff0c;企业的数据安全问题日益凸显。数据安全不仅关乎企业的商业机密&#xff0c;还涉及到客户的隐私和信任。因此&#xff0c;企业必须采取有效的防范措施&#xff0c;确保数据安全。本文将探讨企业防范数据安全的重要性&#xff0c;并介绍一些实…

图像质量评估——PSNR:峰值信噪比和SSIM:结构相似性(纯手撸代码)

目录 PSNR原理代码运行测试结果 SSIM原理代码运行测试结果 总结 PSNR 原理 PSNR 是一种衡量图像质量的指标&#xff0c;它是通过比较原始图像和失真图像之间的差异来计算的。具体来说&#xff0c;PSNR 是通过比较两幅图像的每个像素值来计算的。给定一个大小为 mn 的干净图像…

董事长孙进任职资格获批,盛京银行坎坷向前

11月6日&#xff0c;国家金融监管总局行政许可信息显示&#xff0c;盛京银行&#xff08;HK:02066&#xff09;董事长孙进的任职资格已于近日获准。 作为东北地区成立最早、规模最大的总部银行&#xff0c;盛京银行近年来的发展之路颇为坎坷&#xff0c;在经历了大规模的管理层…

最近的总结(2023.11.8)

菜鸟本来是不打算写文章的&#xff0c;奈何1500的曝光券让我心痒难耐 菜鸟主要是想把这篇博客&#xff08;平凡人的一生的意义是什么&#xff1f;&#xff09;推出去&#xff0c;看看大家的看法&#xff01; 不过既然写&#xff0c;菜鸟自然要好好写&#xff0c;就来聊聊最近…

实现财务自由的十大步骤

一、明确实现财务自由的意义 很多人都希望实现财务自由&#xff0c;但是只有很少人真正想过&#xff1a;我为什么要实现财务自由? 不喜欢干活&#xff0c;不喜欢工作不想让工作束缚自己&#xff0c;不想靠工资收入来维持生活想自由自在&#xff0c;无拘无束环游世界 所以就…

基于袋獾算法的无人机航迹规划-附代码

基于袋獾算法的无人机航迹规划 文章目录 基于袋獾算法的无人机航迹规划1.袋獾搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用袋獾算法来优化无人机航迹规划。 1.袋獾搜索算法 …

AI全栈大模型工程师(十九)Semantic Kernel

文章目录 Semantic KernelSK 的开发进展SK 的生态位SK 基础架构后记 Semantic Kernel 先比较下 Semantic Kernel 和 LangChain。 Semantic KernelLangChain出品公司微软LangChain AI支持语言Python、C#、Java、TypeScriptPython、TypeScript开源协议MITMIT被应用在Microsoft …

第三章:人工智能深度学习教程-基础神经网络(第四节-从头开始的具有前向和反向传播的深度神经网络 – Python)

本文旨在从头开始实现深度神经网络。我们将实现一个深度神经网络&#xff0c;其中包含一个具有四个单元的隐藏层和一个输出层。实施将从头开始&#xff0c;并实施以下步骤。算法&#xff1a; 1. 可视化输入数据 2. 确定权重和偏置矩阵的形状 3. 初始化矩阵、要使用的函数 4. 前…

各省市90米分辨率DEM数据,多图可下载

之前给大家推了30米分辨率dem数据&#xff0c;有些小伙伴反应也需要90米的&#xff0c;于是今天就给大家推荐一个新数据 —— 各省市90米分辨率DEM数据&#xff01; 各省市90米分辨率DEM数据广泛应用于国土资源调查、水利水电工程、地质灾害预警、城市规划等领域&#xff0c;对…

JavaFX入门和网格布局面板的使用,Dao层交互,舞台与场景切换以及其他控件的使用

网格布局 将整个面板划分为若干个格子 , 每个格子的大小是一样的 , 每个格子中可以放置一个控件&#xff08;布局&#xff09; , 类似于表格的方式。在网格布局 中放入控件的时候 , 还需要指定位置。 GridPane gridPane new GridPane(); 我们将要排出这个布局 , 也就是登陆页…

时间序列预测模型实战案例(十)(CNN-GRU-LSTM)通过堆叠CNN、GRU、LSTM实现多元预测和单元预测

本文介绍 本篇博客为大家讲解的是通过组堆叠CNN、GRU、LSTM个数&#xff0c;建立多元预测和单元预测的时间序列预测模型&#xff0c;其效果要比单用GRU、LSTM效果好的多&#xff0c;其结合了CNN的特征提取功能、GRU和LSTM用于处理数据中的时间依赖关系的功能。通过将它们组合在…

快速构建高质量中文APP登录注册页面Figma源文件

在这个数字化时代&#xff0c;移动应用程序&#xff08;APP&#xff09;已经成为我们日常生活中不可或缺的一部分。如果您正在为您的中文APP开发登录注册页面&#xff0c;并寻找高质量的UI设计素材&#xff0c;那么您来对地方了&#xff01;我们为您提供了一个完整的Figma源文件…

Java语言级别8不支持本地枚举和语言级别 ‘8‘ 不支持 内部类中的 static 声明

Java语言级别8不支持本地枚举和语言级别 8 不支持 内部类中的 static 声明 具体报错情况总结 具体报错情况 今天笔者准备在Test下的测试方法创建枚举类的时候&#xff0c;发现出现了报错Java”语言级别8不支持本地枚举“。 然后又试试创建一个类中包含一个枚举类时&#xff0c;…

JavaFX进阶:学生管理系统结构讲解,复合布局集成,表格数据显示

系统介绍 我们会通过一个学生管理系统来学习 其中 , 分为两个角色 老师 Teacher public class Teacher { private Integer id; private String name; private String password; private String gender; } 学生 Student public class Student { private Integer id; priva…

【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割10(测试推理篇)

对于直接将裁剪的patch&#xff0c;一个个的放到训练好的模型中进行预测&#xff0c;这部分代码可以直接参考前面的训练部分就行了。其实说白了&#xff0c;就是验证部分。不使用dataloader的方法&#xff0c;也只需要修改少部分代码即可。 但是&#xff0c;这种方法是不end t…

Busco-真核生物为主基因组质量评估

文章目录 简介Install必须参数谱系数据集输出结果自动谱系选择结果解读完整片段化缺失 自动选择&#xff1a;多domain和污染匹配注意BUSCO报告常用脚本真核Ref 简介 Busco评估基因组质量的核心原理在于通过计算基因组的通用单拷贝标记基因的比例来估计基因组的完整性。其中两个…