MapStruct介绍以及VO、DTO、PO、DO的区别
文章目录
- 一.基本概念
- 1.1VO**(Value Object)值对象**
- 1.2DTO**(Data Transfer Object)数据传输对象**
- 1.3 PO**(Persistant Object)持久对象=**等同于Entity,这俩概念是一致的 或DO
- 1.4 **BO(Business Object)业务对象**
- 1.5注意
- 二.MapStruct
- 三.转换
- 2.1 前置
- 2.2 source类中字段,少于Target类中字段 & 二者相同的属性名称,可直接转换
- 2.3 source类中字段 == 目标类中字段 & 二者所有字段名称完全一样可直接转换
- 2.4 source类中 有个别属性名称 和 target中不一致。需要@mapping()来帮助完成映射
- 2.5 source类中,有一个属性名称和target类中某个属性名称不一致。(但是他们的业务含义是一样的、类型也是一样的,都是list)
- 2.6 source中字段类型 和 target中字段类型不一致 & 类型一致但是需要做逻辑判断后再赋值
- 三.实现方式
- 3.1 List<A> -> List<B>
- 3.2 Target和Source中两个字段类型一致,但是名称不一致,转换时可直接写二者的名称
- 3.3 两个字段名称一致但类型不一致,需要java方法进行转换
- 3.4 两个字段类型一致,名称一致。不需要认为处理
- 3.5 日期格式转换
- 四.QA
- 4.1 convert2XXX(param1,param2);
一.基本概念
1.1VO**(Value Object)值对象**
-
定义:
VO就是展示用的数据,是让人看到的
-
VO和DTO的区别:
VO会对DTO中的值,在展示时,赋予业务解释
举例:
PO中 {"id":23,"gender":"男", "age":35}DTO可能是这样的(相较于PO,仅需要一些字段的值) { "gender":"男", "age":35 }对于业务一来说只需要性别,而且因为是一个古风聊天室,也不能直接展示男,因此经过业务解释业务一的VO是 { "gender":"公子" } 对于业务二来说只需要年龄,而且不需要精确的年龄,因此经过业务解释业务二的VO是 { "age":"30~39" }可以:用dto来接收前端参数,vo返回给前端数据
1.2DTO**(Data Transfer Object)数据传输对象**
-
定义
在前端,他的存在形式通常是js里面的对象(也可以简单理解成json),也就是通过ajax请求的那个数据体。
在后端,他的存在形式是java对象,也就是在controller里面定义的那个东东,通常在后端不需要关心怎么从json转成java对象的
json从前端传递到后端,然后json->java对象给后端;java对象在后端 -> json形式传递给前端
-
服务和服务之间调用的传输对象能叫DTO吗:
如果服务和服务之间相对独立,那就可以叫DTO;
如果服务和服务之间不独立,每个都不是一个完整的业务模块,拆开可能仅仅是因为计算复杂度或者性能的问题,只能是BO
-
使用sop:
用 DTO 的好处有两个,一是能避免传递过多的无用数据,提高数据的传输速度;
二是能隐藏后端的表结构。常见的用法是:将请求的数据或属性组装成一个 RequestDTO,再将响应的数据或属性组装成一个 ResponseDTO
接收前端传递的数据使用DTO、展示给前端的数据使用VO
1.3 PO**(Persistant Object)持久对象=**等同于Entity,这俩概念是一致的 或DO
-
定义:
简单说PO就是数据库中的记录,一个PO的数据结构对应着库中表的结构,表中的一条记录就是一个PO对象
-
特点
通常PO里面除了get,set之外没有别的方法
1.4 BO(Business Object)业务对象
-
定义:
1.BO就是PO的组合,每份简历都包括教育经历、项目经历等,我们可以让教育经历和项目经历分别对应一个 PO,这样在我们建立对应求职简历的 BO 对象处理简历的时候,让每个 BO 都包含这些 PO 即可。(在形成BO期间即简历初版之前,可以对教育经历PO做一些计算、处理(比如:PO中时间为2011-2016就读于NJUPT,则形成BO过程中,可做计算本科时间5年 = 2016-2011)。简历初版BO中许多内容其实是不需要的,没必要展示在简历上,比如教育经历PO中你的老师是谁,BO中也有,这些无需展示在简历上。这样优化后的简历即为DTO。然后DTO仍然不优美,可以继续优化成最终简历VO(比如:DTO中学历展示为:研究生字段,VO中可以转换为 硕士字段)
PO -> BO -> DTO(如果和前端交互可以用VO)
比如我们有一个交易订单表,含有 25 个字段,那么其对应的 PO 就有 25 个属性,但我们的页面上只需要显示 5 个字段,因此没有必要把整个 PO 对象传递给客户端,这时我们只需把仅有 5 个属性的 DTO 把结果传递给客户端即可,而且如果用这个对象来对应界面的显示对象,那此时它的身份就转为 VO。
2.比如处理一个人的业务逻辑,该人会睡觉,吃饭,工作,上班等等行为,还有可能和别人发关系的行为,处理这样的业务逻辑时,我们就可以针对BO去处理。
-
特点:
BO是一个业务对象,一类业务就会对应一个BO,数量上没有限制,而且BO会有很多业务操作,也就是说除了get,set方法以外,BO会有很多针对自身数据进行计算的方法
BO来源:由PO拼装而成;Mybatis跳过PO直接生成BO
-
与DTO的区别
这两个的区别主要是就是字段的删减
BO可能会含有很多接口对外所不需要的数据,因此DTO需要在BO的基础上,只要自己需要的数据,然后对外提供在这个关系上,DTO通常不会有数据内容的变化(原本展示:23pcs (1箱子 + 3件))补:1箱20件
内容变化(现在一箱10件)要么在BO内部业务计算的时候完成,要么在解释VO的时候完成(现在业务展示需要发生变化,但是不在DTO计算,而是在BO进行计算, 13 = 1 * 10 + 3 ),对外展示还是这个字段。内部计算逻辑在BO完成
1.5注意
-
DTO 和 BO
工具类的系统和一些业务不是很复杂的系统DTO是可以和BO合并成一个,当业务扩展的时候注意拆分就行
PO -> DTO(如果和前端交互可以用VO)
-
DTO和VO
VO是可以第一个优化掉的,展示业务不复杂的可以压根儿不要,直接用DTO
二.MapStruct
参考:http://www.tianshouzhi.com/api/tutorials/mapstruct/291
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;import java.util.List;
import java.util.Objects;@Mapper(componentModel = "spring")
public interface MyrConverter {MyrConverter INSTANCE = Mappers.getMapper(MyrConverter.class);//事例1@Mappings({@Mapping(target = "billNo", source = "a.billNo"),@Mapping(target = "billType", expression = "java(null!=a.getBillType() ? (byte) a.getBillType().getCode() : null)"),@Mapping(target = "createAt", expression = "java(null!=a.getCreateAt() ? a.getCreateAt().getTime() : null)"),B convert2B(A a);List<B> convert2BList(List<A> sources);//事例2@Mappings({@Mapping(target = "outboundBillNo", source = "useBizBillNo"),@Mapping(target = "shipperId", source = "poiId"),@Mapping(target = "receiverId", source = "useReciverId"),@Mapping(target = "takeDate", expression = "java(getTakeDate(a))")})B convert2B(A a);List<B> convert2BList(List<A> aList);default Long getPlanTakeDate(A a) {if (Objects.nonNull(a.getTakeDate())) {return a.getTakeDate().getTime();}return null;}}
三.转换
2.1 前置
-
Source:需要被转换的类
-
Target:期望转换成的类
2.2 source类中字段,少于Target类中字段 & 二者相同的属性名称,可直接转换
-
souce类中字段:
Long poiId
String containerCode
@Data public class Souce {private Long poiId;private String containerCode; }target类字段:
Long poiId
String containerCode
List*<Byte>* statusList;
@Data
public class Target {private Long poiId;private String containerCode;private List<Byte> statusList;
}
-
source类中字段少于target类中字段,且相同的两个属性名称一样。可直接转换,无需@mapping()
Target convert2Target(Source source);
2.3 source类中字段 == 目标类中字段 & 二者所有字段名称完全一样可直接转换
-
source
B
-
target
A
-
A-> B& 二者属性完全一致,且属性名称也相同
B convert2B(A source);
2.4 source类中 有个别属性名称 和 target中不一致。需要@mapping()来帮助完成映射
@Mappings({@Mapping(target = "taskList", source = "source.pickingTaskList"),})B convert2B(A source);
2.5 source类中,有一个属性名称和target类中某个属性名称不一致。(但是他们的业务含义是一样的、类型也是一样的,都是list)
| A | B |
|---|---|
| Long skuId | Long skuId |
| List*<DTO>* dtoList; | List*<VO>* voList; |
- 这时,为了将A-> B,即需要Mappings()
2.6 source中字段类型 和 target中字段类型不一致 & 类型一致但是需要做逻辑判断后再赋值
- 字段类型不一致,需要express表达式转换
@Mappings({@Mapping(target = "containerStatusDesc", expression = "java(null!=source.getUseStatus() ? source.getUseStatus().getDesc() : \"\")"),@Mapping(target = "shippedAt", expression = "java(null!=source.getShippingAt() ? source.getShippingAt().getTime() : null)"),@Mapping(target = "totalSkuQuantity", expression = "java(null == source.getTotalQuantity() ? \"0\" : source.getTotalQuantity().stripTrailingZeros().toPlainString())")})B convert2B(A source);
-
source中字段类型为:Date shippingAt
-
target你要转换的类中相同业务含义的属性为: Long shippedAt
-
所以,需要判断一下,若Date shippingAt不为null即null!=containerUseFlow.getShippingAt(),则将Date先转换为long,再赋值给Long shippedAt 否则为null
-
source中字段类型为:BigDecimal totalQuantity;
-
target你要转换的类中相同业务含义的属性为:String totalSkuQuantity;
-
所以,需要判断一下,若BigDecimal totalQuantity;不为null,则将BigDecimal换为String,即.stripTrailingZeros().toPlainString()
若为null,则赋值 “0” 应该是0的意思
三.实现方式
3.1 List -> List
- convert2Dto()
- 定义getPlanTakeDate()
- 在接口中实现default getPlanTakeDate()方法即可
@Mappings(value = {@Mapping(target = "planTakeDate", expression = "java(getPlanTakeDate(source))")})B convert2Dto(A source);List<B> convert2Dtos(List<A> sources);
3.2 Target和Source中两个字段类型一致,但是名称不一致,转换时可直接写二者的名称
@Mapping*(target = “totalPcs”, source = “skuPcs”)*
3.3 两个字段名称一致但类型不一致,需要java方法进行转换
@Mapping*(*target = "planTakeDate", expression = "java(getPlanTakeDate(source.planTakeDate))"*)*
3.4 两个字段类型一致,名称一致。不需要认为处理
3.5 日期格式转换
@MdpCopying(target = "ctime", dateFormat = "yyyy年MM月dd日HH:mm:ss", source = "ctime")@MdpCopying(target = "utime", dateFormat = "yyyy年MM月dd日HH:mm:ss", source = "utime")B copy2DO(A source);List<B> copy2DOs(List<A> source);
四.QA
4.1 convert2XXX(param1,param2);
MapStruct 可以自动为您执行此操作。然而,它不能处理多参数方法(原则上它将源映射到目标)。所以报错:返回类型 java.util.List 是一个抽象类或接口。提供非抽象/非接口结果类型或工厂方法。
解决:
参考文档:
https://zhuanlan.zhihu.com/p/102389552
相关文章:
MapStruct介绍以及VO、DTO、PO、DO的区别
文章目录 一.基本概念1.1VO**(Value Object)值对象**1.2DTO**(Data Transfer Object)数据传输对象**1.3 PO**(Persistant Object)持久对象**等同于Entity,这俩概念是一致的 或DO1.4 **BO&#x…...
记一次hyperf框架封装swoole自定义进程
背景 公司准备引入swoole和rabbitmq来处理公司业务。因此,我引入hyperf框架,想用swoole的多进程来实现。 自定义启动服务封装 <?php /*** 进程启动服务【manager】*/ declare(strict_types1);namespace App\Command;use Swoole; use Swoole\Proce…...
多输入多输出 | MATLAB实现GA-BP遗传算法优化BP神经网络多输入多输出
多输入多输出 | MATLAB实现GA-BP遗传算法优化BP神经网络多输入多输出 目录 多输入多输出 | MATLAB实现GA-BP遗传算法优化BP神经网络多输入多输出预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 多输入多输出 | MATLAB实现GA-BP遗传算法优化BP神经网络多输入多输出…...
李宏毅机器学习笔记-transformer
transformer是什么呢?是一个seq2seq的model。具体应用如上图所示,输入和输出的序列长度不固定,由model自己决定。 语音翻译指的是,直接输入一段语音信号,例如英文,输出的直接是翻译之后的中文。 seq2seq如…...
基于Java的酒店管理系统
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...
Go语言的单元测试与基准测试详解
文章目录 单元测试基准测试 单元测试 以一个加法函数为例,对其进行单元测试。 首先编写add.go文件: //add.go package mainfunc add(a, b int) int {return a b }其次编写add_test.go文件,在go语言中,测试文件均已_test结尾&a…...
【多态】为什么析构函数的名称统一处理为destructor?
析构函数的名称统一处理为destructor的目的是为了解决析构函数的重写。 而这又引出了一个问题:为什么要进行析构函数的重写? 是为了下面这种情况: class Person { public:~Person() { cout << "~Person" << endl; } }…...
6.4 Case Studies - A Simple Logging Archive Class
下面这段内容介绍了一个示例,目的是帮助澄清"归档概念(Archive Concept)"的用法,以便用户可以实现自己的归档类。simple_log_archive.hpp 实现了一个简单但实用的归档类,用于将任何可序列化类型以可读的格式…...
【深度学习实验】前馈神经网络(九):整合训练、评估、预测过程(Runner)
目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. __init__(初始化) 2. train(训练) 3. evaluate(评估) 4. predict(预测) 5. save_model 6. load_model 7. 代码整合 一、实验介绍 二、实验环境 本系列实验使用…...
002-第一代硬件系统架构确立及产品选型
第一代硬件系统架构确立及产品选型 文章目录 第一代硬件系统架构确立及产品选型项目介绍摘要硬件架构硬件结构选型及设计单片机选型上位机选型扯点别的 关键字: Qt、 Qml、 信号采集机、 数据处理、 上位机 项目介绍 欢迎来到我们的 QML & C 项目ÿ…...
Go基础语法:指针和make和new
8 指针、make、new 8.1 指针(pointer) Go 语言中没有指针操作,只需要记住两个符号即可: & 取内存地址* 根据地址取值 package mainimport "fmt"func main() {a : 18// 获取 a 的地址值并复制给 pp : &a// …...
039_小驰私房菜_Camera perfermance debug
全网最具价值的Android Camera开发学习系列资料~ 作者:8年Android Camera开发,从Camera app一直做到Hal和驱动~ 欢迎订阅,相信能扩展你的知识面,提升个人能力~ 一、抓取trace 1. adb shell "echo vendor.debug.trace.perf=1 >> /system/build.prop" 2. …...
Caché for Windows安装及配置
本文介绍在Windows上安装Cach的操作步骤。本文假设用户熟悉Windows目录结构、实用程序和命令。本文包含如下主要部分: 1)Cach安装...
代码随想录算法训练营20期|第四十六天|动态规划part08|● 139.单词拆分 ● 关于多重背包,你该了解这些! ● 背包问题总结篇!
139.单词拆分 感觉这个板块要重新刷,完全没有印象 class Solution {public boolean wordBreak(String s, List<String> wordDict) {Set<String> set new HashSet<>(wordDict);boolean[] dp new boolean[s.length() 1];dp[0] true;for (int i…...
系统安装(一)CentOS 7 本地安装
CentOS与Ubuntu并称为Linux最著名的两个发行版,但由于笔者主要从事深度学习图像算法工作,Ubuntu作为谷歌和多数依赖库的亲儿子占据着最高生态位。但最近接手的一个项目里,甲方指定需要在CentOS7上运行项目代码,笔者被迫小小cos了一…...
obsidian使用指南
插入代码块快捷键设置 插入代码块 用英文搜索快捷键名字 英文搜索的【Insert code block】对应的是 (6个点) 中文搜索的【代码块】对应的是 (2个点) 查看word、excel等非md文件设置 电脑端obsidian->设置->文件与链接->检测所有类型文件->…...
【ardunio】青少年机器人四级实操代码(2023年9月)
目录 一、题目 二、示意图 三、流程图 四、硬件连接 1、舵机 2、超声波 3、LED灯 五、程序 一、题目 实操考题(共1题,共100分) 1. 主题: 迎宾机器人 器件:Atmega328P主控板1块,舵机1个,超声波传感器1个&…...
MYSQL的存储过程
存储过程 存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。存储过程思想上很简单,就是…...
[kubernetes/docker] failed to resolve reference ...:latest: not found
问题描述: pod一直pending, kubectl describe pod ... 显示: Warning Failed 9s (x3 over 63s) kubelet Failed to pull image "mathemagics/my-kube-scheduler": rpc error: code NotFound desc failed to pull and unpack image "docker…...
彻底解决win11系统0x80070032
经过各种尝试,终于找到原因。第一个是电脑加密软件,第二个是需要的部分功能没有开启,第三个BIOS设置。个人觉得第三个不重要。 解决方法 笔记本型号 笔记本型号是Thinkpad T14 gen2。进入BIOS的按键是按住Enter键。 1、关闭山丽防水墙服务…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
DAY 45 超大力王爱学Python
来自超大力王的友情提示:在用tensordoard的时候一定一定要用绝对位置,例如:tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾: tensorboard的发展历史和原理tens…...
Python环境安装与虚拟环境配置详解
本文档旨在为Python开发者提供一站式的环境安装与虚拟环境配置指南,适用于Windows、macOS和Linux系统。无论你是初学者还是有经验的开发者,都能在此找到适合自己的环境搭建方法和常见问题的解决方案。 快速开始 一分钟快速安装与虚拟环境配置 # macOS/…...
VSCode 没有添加Windows右键菜单
关键字:VSCode;Windows右键菜单;注册表。 文章目录 前言一、工程环境二、配置流程1.右键文件打开2.右键文件夹打开3.右键空白处打开文件夹 三、测试总结 前言 安装 VSCode 时没有注意,实际使用的时候发现 VSCode 在 Windows 菜单栏…...
