ReturnTmp's Blog ReturnTmp's Blog
首页
基础课程
编程语言
框架技术
运维笔记
人工智能
随笔摘录
  • 友链
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

ReturnTmp

分享有趣好玩的计算机知识
首页
基础课程
编程语言
框架技术
运维笔记
人工智能
随笔摘录
  • 友链
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • GLIBC_2.28 not found 问题解决
  • Monorepo 多项目单仓库
  • Renovate 第三方依赖更新监控
  • 【IDEA】Maven 构建项目生成文件解析
  • K8s

  • Zotero 使用指南
  • package-lock.json 是否提交问题
  • 『ARM』和『x86』处理器架构解析指南
  • GlassFish 安装配置
  • 手把手教你安装配置『Oracle Database 19c』
  • Oracle Database 19c 彻底卸载
  • 语雀故障回顾
  • OpenStack 云计算平台 Nova 计算服务学习指南
  • Vue devServer 教程
  • Swagger 导出 API 文档
  • MapStruct POJO 映射框架指南
    • 前言
    • 问题
    • 结构
    • 手动转换
    • 工具类转换
    • 映射框架转换
    • MapStruct
      • 简介
      • 安装
      • 示例
    • 补充
      • POJO
      • 简介
      • 转换对象
      • GitHub 单独下载文件夹
      • GitZip
      • DownGit
    • 参考链接
  • IDEA 代码热部署和热加载
  • SpringBoot 启动参数配置
  • Nacos 入门指南
  • seleuim 指南
  • Spring 服务降级熔断
  • Maven BOM 解析
  • .vscode 文件夹
  • Spring Security Token 认证
  • SpringBoot 基于 Actuator 和 Admin 实现应用监控管理
  • SPM/SCM 流量跟踪体系
  • Netty 入门
  • Flyway 数据库版本管理实战指南
  • Swagger 2 和 3 安装区别
  • MP 配置分页
  • MySQL 分库分表
  • Git Commit 提交规范,变更日志、版本发布自动化和 Emoji 提交标准
  • VSCode 插件 i18n Ally 进行国家化配置
  • Vue3 组合式 全局挂载
  • TS 教程
  • 架构解析:同城双活、异地多活、单元化架构
  • Spring 跨域配置
  • SpringCloud 微服务实战
  • Sentinel 流量治理组件教程
  • leetcode 上分
  • JMeter 压测
  • Netty IM 系统
  • IDEA 插件开发
  • SpringBoot 邮件服务 集成配置 详解
  • Maven 依赖包冲突问题解决
  • 社区项目 forest 修改
  • Maven 项目命名规范
  • 新版 PyCharm 设置 Conda 虚拟环境
  • 框架工具
ReturnTmp
2023-11-03
目录

MapStruct POJO 映射框架指南

# 前言

本文将会主要讲解后端开发中 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;
}
1
2
3
4
5
6
7
8
9
10
11

DTO

@Data
public class OrderDTO {
    private String orderId;
    private String orderType;
    private int orderStatus;
    private Date createdAt;
}
1
2
3
4
5
6
7

VO

@Data
public class OrderVO extends OrderDTO{
    private String orderTypeName;
    private String orderStatusName;
}
1
2
3
4
5

# 手动转换

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

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

OrderDTO dto = new OrderDTO();
dto.setOrderId(entity.getOrderId());
dto.setOrderType(entity.getOrderType());
dto.setOrderStatus(entity.getOrderStatus());
dto.setCreatedAt(entity.getCreatedAt());
1
2
3
4
5

# 工具类转换

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

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

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

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

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

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

# 映射框架转换

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

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

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

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

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

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

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

# MapStruct

# 简介

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

官方文档:MapStruct – Java bean mappings, the easy way! (opens new window)

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

# 安装

官方文档:Installation – MapStruct (opens new window)

首先我们需要向 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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 示例

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

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

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

Car.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {

    private String make;
    private int numberOfSeats;
    private CarType type;
    
}
1
2
3
4
5
6
7
8
9
10

CarType.java

public enum CarType {
    SPORTS, OTHER;
}
1
2
3

CarDto.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarDto {

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

}
1
2
3
4
5
6
7
8
9
10

CarMapper.java

@Mapper
public interface CarMapper {
 
    CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
 
    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car);
}
1
2
3
4
5
6
7
8

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");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

但是 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>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

我们需要确保 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) (opens new window)
  • Plain old Java object - Wikipedia (opens new window)

# 转换对象

我们可以辅助理解 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) (opens new window)

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

image-20231104112214588

# DownGit

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

# 参考链接

  • Quick Guide to MapStruct | Baeldung (opens new window)
  • POJO、PO、DTO、VO、BO ? EJB、EntityBean (opens new window)
  • 什么是JavaBean? - JYRoy - 博客园 (cnblogs.com) (opens new window)
  • JavaBean - 廖雪峰的官方网站 (liaoxuefeng.com) (opens new window)
  • 微服务中VO、DTO、Entity间的相互转换处理 (opens new window)
  • Github | 如何在Github上只下载一个文件或文件夹 (opens new window)
编辑 (opens new window)
上次更新: 2024/03/24, 08:31:38
Swagger 导出 API 文档
IDEA 代码热部署和热加载

← Swagger 导出 API 文档 IDEA 代码热部署和热加载→

最近更新
01
百度网盘加速
03-24
02
新版 PyCharm 设置 Conda 虚拟环境
03-24
03
腾讯云域名转到阿里云
03-24
更多文章>
Theme by Vdoing | Copyright © 2023-2024 ReturnTmp | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式