当前位置: 首页 > news >正文

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)

AB
Long skuIdLong 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**&#xff08;Value Object&#xff09;值对象**1.2DTO**&#xff08;Data Transfer Object&#xff09;数据传输对象**1.3 PO**&#xff08;Persistant Object&#xff09;持久对象**等同于Entity&#xff0c;这俩概念是一致的 或DO1.4 **BO&#x…...

记一次hyperf框架封装swoole自定义进程

背景 公司准备引入swoole和rabbitmq来处理公司业务。因此&#xff0c;我引入hyperf框架&#xff0c;想用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是什么呢&#xff1f;是一个seq2seq的model。具体应用如上图所示&#xff0c;输入和输出的序列长度不固定&#xff0c;由model自己决定。 语音翻译指的是&#xff0c;直接输入一段语音信号&#xff0c;例如英文&#xff0c;输出的直接是翻译之后的中文。 seq2seq如…...

基于Java的酒店管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...

Go语言的单元测试与基准测试详解

文章目录 单元测试基准测试 单元测试 以一个加法函数为例&#xff0c;对其进行单元测试。 首先编写add.go文件&#xff1a; //add.go package mainfunc add(a, b int) int {return a b }其次编写add_test.go文件&#xff0c;在go语言中&#xff0c;测试文件均已_test结尾&a…...

【多态】为什么析构函数的名称统一处理为destructor?

析构函数的名称统一处理为destructor的目的是为了解决析构函数的重写。 而这又引出了一个问题&#xff1a;为什么要进行析构函数的重写&#xff1f; 是为了下面这种情况&#xff1a; class Person { public:~Person() { cout << "~Person" << endl; } }…...

6.4 Case Studies - A Simple Logging Archive Class

下面这段内容介绍了一个示例&#xff0c;目的是帮助澄清"归档概念&#xff08;Archive Concept&#xff09;"的用法&#xff0c;以便用户可以实现自己的归档类。simple_log_archive.hpp 实现了一个简单但实用的归档类&#xff0c;用于将任何可序列化类型以可读的格式…...

【深度学习实验】前馈神经网络(九):整合训练、评估、预测过程(Runner)

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. __init__(初始化) 2. train(训练) 3. evaluate(评估) 4. predict(预测) 5. save_model 6. load_model 7. 代码整合 一、实验介绍 二、实验环境 本系列实验使用…...

002-第一代硬件系统架构确立及产品选型

第一代硬件系统架构确立及产品选型 文章目录 第一代硬件系统架构确立及产品选型项目介绍摘要硬件架构硬件结构选型及设计单片机选型上位机选型扯点别的 关键字&#xff1a; Qt、 Qml、 信号采集机、 数据处理、 上位机 项目介绍 欢迎来到我们的 QML & C 项目&#xff…...

Go基础语法:指针和make和new

8 指针、make、new 8.1 指针&#xff08;pointer&#xff09; Go 语言中没有指针操作&#xff0c;只需要记住两个符号即可&#xff1a; & 取内存地址* 根据地址取值 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.单词拆分 感觉这个板块要重新刷&#xff0c;完全没有印象 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最著名的两个发行版&#xff0c;但由于笔者主要从事深度学习图像算法工作&#xff0c;Ubuntu作为谷歌和多数依赖库的亲儿子占据着最高生态位。但最近接手的一个项目里&#xff0c;甲方指定需要在CentOS7上运行项目代码&#xff0c;笔者被迫小小cos了一…...

obsidian使用指南

插入代码块快捷键设置 插入代码块 用英文搜索快捷键名字 英文搜索的【Insert code block】对应的是 (6个点) 中文搜索的【代码块】对应的是 &#xff08;2个点&#xff09; 查看word、excel等非md文件设置 电脑端obsidian->设置->文件与链接->检测所有类型文件->…...

【ardunio】青少年机器人四级实操代码(2023年9月)

目录 一、题目 二、示意图 三、流程图 四、硬件连接 1、舵机 2、超声波 3、LED灯 五、程序 一、题目 实操考题(共1题&#xff0c;共100分) 1. 主题&#xff1a; 迎宾机器人 器件&#xff1a;Atmega328P主控板1块&#xff0c;舵机1个&#xff0c;超声波传感器1个&…...

MYSQL的存储过程

存储过程 存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合&#xff0c;调用存储过程可以简化应用开发人员的很多工作&#xff0c;减少数据在数据库和应用服务器之间的传输&#xff0c;对于提高数据处理的效率是有好处的。存储过程思想上很简单&#xff0c;就是…...

[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

经过各种尝试&#xff0c;终于找到原因。第一个是电脑加密软件&#xff0c;第二个是需要的部分功能没有开启&#xff0c;第三个BIOS设置。个人觉得第三个不重要。 解决方法 笔记本型号 笔记本型号是Thinkpad T14 gen2。进入BIOS的按键是按住Enter键。 1、关闭山丽防水墙服务…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...

webpack面试题

面试题&#xff1a;webpack介绍和简单使用 一、webpack&#xff08;模块化打包工具&#xff09;1. webpack是把项目当作一个整体&#xff0c;通过给定的一个主文件&#xff0c;webpack将从这个主文件开始找到你项目当中的所有依赖文件&#xff0c;使用loaders来处理它们&#x…...

轻量级Docker管理工具Docker Switchboard

简介 什么是 Docker Switchboard &#xff1f; Docker Switchboard 是一个轻量级的 Web 应用程序&#xff0c;用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器&#xff0c;使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...

欢乐熊大话蓝牙知识17:多连接 BLE 怎么设计服务不会乱?分层思维来救场!

多连接 BLE 怎么设计服务不会乱&#xff1f;分层思维来救场&#xff01; 作者按&#xff1a; 你是不是也遇到过 BLE 多连接时&#xff0c;调试现场像网吧“掉线风暴”&#xff1f; 温度传感器连上了&#xff0c;心率带丢了&#xff1b;一边 OTA 更新&#xff0c;一边通知卡壳。…...