优雅使用 MapStruct 进行类复制
前言
在项目中,常常会遇到从数据库读取数据后不能直接返回给前端展示的情况,因为还需要对字段进行加工,比如去除时间戳记录、隐藏敏感数据等。传统的处理方式是创建一个新类,然后编写大量的 get/set 方法进行赋值,若字段很多,这无疑是一场噩梦,而且还容易出现遗漏的情况。
我们都清楚,随着工程日益成熟,模块划分会越发细致。实体类通常存放在 domain 中,但最好不要让 domain 工程被其他工程依赖。所以,当其他工程需要获取实体类数据时,就需要在各自工程中编写 model。自定义 model 能够根据自身业务需求映射相应的实体属性。如此一来,这个映射工作似乎并不简单。
这个时候,我们可以使用MapStruct,
在企业级应用中,经常需要在不同类型的对象(如 DTO 和 DO、VO 和 PO 等)之间进行转换。MapStruct 通过在编译时基于接口定义生成转换代码,大大简化了这个过程。例如,从一个包含用户注册信息的 DTO 转换为一个用于业务逻辑处理的 DO 时,只需要定义一个 MapStruct 接口。
引入依赖
<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><lombok.version>1.18.34</lombok.version><mapstruct.version>1.6.2</mapstruct.version>
</properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>${mapstruct.version}</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${mapstruct.version}</version><scope>provider</scope></dependency></dependencies>
org.mapstruct:mapstruct
- 这是MapStruct的核心库。它包含了用于定义映射接口的注解(如
@Mapper、@Mapping等)以及在运行时执行映射操作所需的类型和接口。 - 开发人员在Java代码中使用这些注解来定义对象之间的映射关系。例如,在不同的领域对象(Domain Object)、数据传输对象(Data Transfer Object)、视图对象(View Object)等之间的转换映射。这个库提供了基本的框架,使得可以按照声明式的方式指定对象属性如何从一个对象映射到另一个对象。
- 当执行映射操作时(例如,通过调用由MapStruct生成的映射器实例的映射方法),这个库中的代码会协调映射过程,根据定义的映射规则进行数据的转换。
org.mapstruct:mapstruct - processor
- 这个依赖是MapStruct的注解处理器(Annotation Processor)。在Java编译过程中,它会查找带有MapStruct注解(来自
org.mapstruct:mapstruct库)的接口或抽象类。 - 一旦找到这样的接口或抽象类,它会根据定义的映射关系(通过
@Mapper、@Mapping等注解)生成具体的映射实现类。这个生成过程是在编译时进行的,生成的代码会被编译到最终的字节码中。 - 例如,如果有一个定义了从
SourceObject到TargetObject映射关系的@Mapper接口,mapstruct - processor会生成一个实现该接口的类,这个类包含了将SourceObject的属性值按照指定规则赋给TargetObject属性的具体代码。这里的<scope>provider</scope>表示该依赖是一个提供运行时环境所需的组件,主要用于在编译时提供代码生成功能。
定义实体
定义两个实体,字段上略微有些差别
import java.util.Date;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private long id;private String name;private int age;private String password;private Date createTime;
}
import lombok.Data;@Data
public class UserVO {private Long id;private String name;private Integer age;private String code;private String hello;private String createTime;
}
定义转换的mapper
如果是spring项目,用@Mapper(componentModel = "spring"),生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入
package com.zxy.demo;import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;@Mapper
public interface UserMapper {public static final UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);static String hello(User user) {return "hello " + user.getName();}@Mapping(target = "createTime", source = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")@Mapping(target = "code", expression="java(\"xx-\" + user.getId())")@Mapping(target = "hello", expression = "java(UserMapper.hello(user))")UserVO toUserVO(User user);}
如果所有的字段都一样,用@Mappings({})。
不一样的用target+source,需要特殊处理的可以用expression。
在 MapStruct 中,expression是一个强大的功能,用于在对象映射过程中执行自定义的表达式。它允许开发人员在映射规则中使用 Java 表达式来处理复杂的映射逻辑,而不仅仅是简单的属性到属性的映射。
这在源对象和目标对象的属性之间存在复杂关系,或者需要进行额外的计算、逻辑判断等情况时非常有用。
来个单测运行一下
package com.zxy.demo;import java.util.Date;import org.junit.Assert;
import org.junit.Test;public class UserTest {@Testpublic void ok() {User u = new User();u.setId(1);u.setAge(10);u.setName("zxy");u.setPassword("123456");u.setCreateTime(new Date());Assert.assertEquals(10, u.getAge());Assert.assertEquals("zxy", u.getName());System.out.println(new User(1, "zxy", 12, "123456", new Date()));UserVO vo = UserMapper.INSTANCE.toUserVO(u);System.out.println("vo: "+vo);Assert.assertEquals("zxy", vo.getName());Assert.assertEquals("xx-1", vo.getCode());Assert.assertEquals("hello zxy", vo.getHello());}
}
简单看一下生成的代码

// Source code is unavailable, and was generated by the Fernflower decompiler.
package com.zxy.demo;import java.text.SimpleDateFormat;public class UserMapperImpl implements UserMapper {public UserVO toUserVO(User user) {if (user == null) {return null;} else {UserVO userVO = new UserVO();if (user.getCreateTime() != null) {userVO.setCreateTime((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(user.getCreateTime()));}userVO.setId(user.getId());userVO.setName(user.getName());userVO.setAge(user.getAge());userVO.setCode("xx-" + user.getId());userVO.setHello(UserMapper.hello(user));return userVO;}}
}相关文章:
优雅使用 MapStruct 进行类复制
前言 在项目中,常常会遇到从数据库读取数据后不能直接返回给前端展示的情况,因为还需要对字段进行加工,比如去除时间戳记录、隐藏敏感数据等。传统的处理方式是创建一个新类,然后编写大量的 get/set 方法进行赋值,若字…...
第19周JavaWeb编程实战-MyBatis实现OA系统 1-OA系统
办公OA系统项目开发 课程简介 本课程将通过慕课办公OA平台的开发,讲解实际项目开发中必须掌握的技能和设计技巧。课程分为三个主要阶段: 需求说明及环境准备: 基于RBAC的访问控制模块开发: 多级请假审批流程开发: …...
仿黑神话悟空跑动-脚下波纹特效(键盘wasd控制走动)
vue使用three.js实现仿黑神话悟空跑动-脚下波纹特效 玩家角色的正面始终朝向鼠标方向,且在按下 W 键时,玩家角色会朝着鼠标方向前进 空格建跳跃 <template><div ref"container" class"container" click"onClick"…...
`torch.utils.data`模块
在PyTorch中,torch.utils.data模块提供了许多有用的工具来处理和加载数据。以下是对您提到的DataLoader, Subset, BatchSampler, SubsetRandomSampler, 和 SequentialSampler的详细解释以及使用示例。 1. DataLoader DataLoader是PyTorch中用于加载数据的一个非常…...
深入理解 `strncat()` 函数:安全拼接字符串
目录: 前言一、 strncat() 函数的基本用法二、 示例代码三、 strncat() 与 strcat() 的区别四、 注意事项五、 实际应用场景总结 前言 在C语言中,字符串操作是编程中非常常见的需求。strncat() 函数是标准库中用于字符串拼接的一个重要函数,…...
OpenCV_自定义线性滤波(filter2D)应用详解
OpenCV filter2D将图像与内核进行卷积,将任意线性滤波器应用于图像。支持就地操作。当孔径部分位于图像之外时,该函数根据指定的边界模式插值异常像素值。 卷积核本质上是一个固定大小的系数数组,数组中的某个元素被作为锚点(一般…...
设计模式之装饰模式(Decorator)
前言 这个模式带给我们有关组合跟继承非常多的思考 定义 “单一职责” 模式。动态(组合)的给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少…...
大数据-146 Apache Kudu 安装运行 Dockerfile 模拟集群 启动测试
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…...
React入门准备
React是什么 React是一个用于构建用户界面的JavaScript框架,用于构建“可预期的”和“声明式的”Web用户界面,特别适合于构建那些数据会随时间改变的大型应用的用户界面。 它起源于Facebook的内部项目,因为对市场上所有JavaScript MVC框架都…...
robomimic基础教程(四)——开源数据集
robomimic开源了大量数据集及仿真环境,数据集标准格式为HDF5 目录 一、基础要求 二、使用步骤 1. 下载数据集 2. 后处理 3. 训练 4. 查看训练结果 三、HDF5数据集结构与可视化 1. 数据集结构 (1)根级别(data 组 group&a…...
胤娲科技:AI界的超级充电宝——忆阻器如何让LLM告别电量焦虑
当AI遇上“记忆橡皮擦”,电量不再是问题! 嘿,朋友们,你们是否曾经因为手机电量不足而焦虑得像个无头苍蝇?想象一下,如果这种“电量焦虑”也蔓延到了AI界, 特别是那些聪明绝顶但“耗电如喝水”的…...
前端大模型入门:使用Transformers.js手搓纯网页版RAG(二)- qwen1.5-0.5B - 纯前端不调接口
书接上文,本文完了RAG的后半部分,在浏览器运行qwen1.5-0.5B实现了增强搜索全流程。但受限于浏览器和模型性能,仅适合于研究、离线和高隐私场景,但对前端小伙伴来说大模型也不是那么遥不可及了,附带全部代码,…...
K-means聚类分析对比
K-means聚类分析,不同K值聚类对比,该内容是关于K-means聚类分析的,主要探讨了不同K值对聚类结果的影响。K-means聚类是一种常见的数据分析方法,用于将数据集划分为K个不同的类别。在这个过程中,选择合适的K值是非常关键…...
tar命令:压缩、解压的好工具
一、命令简介 用途: tar 命令用于创建归档文件(tarball),以及从归档文件中提取文件。 标签: 文件管理,归档。 特点: 归档文件可以保留原始文件和目录的层次结构,通常使用 .tar …...
Mac电脑上最简单安装Python的方式
背景 最近换了一台新的 MacBook Air 电脑,所有的开发软件都没有了,需要重新配环境,而我现在最常用的开发程序就是Python。这篇文章记录一下我新Mac电脑安装Python的全过程,也给大家一些思路上的提醒。 以下是我新电脑的配置&…...
Linux基础命令cd详解
cd(change directory)命令是 Linux 中用于更改当前工作目录的基础命令。它没有很多复杂的参数,但它的使用非常频繁。以下是 cd 命令的详细说明及示例。 基本语法 cd [选项] [路径] 常用选项 -L : 使用逻辑路径(默认选项&…...
【大模型对话 的界面搭建-Open WebUI】
Open WebUI 前身就是 Ollama WebUI,为 Ollama 提供一个可视化界面,可以完全离线运行,支持 Ollama 和兼容 OpenAI 的 API。 github网址 https://github.com/open-webui/open-webui安装 第一种 docker安装 如果ollama 安装在同一台服务器上&…...
如何在算家云搭建text-generation-webui(文本生成)
一、text-generation-webui 简介 text-generation-webui 是一个流行的用于文本生成的 Gradio Web UI。支持 transformers、GPTQ、AWQ、EXL2、llama.cpp (GGUF)、Llama 模型。 它的特点如下, 3 种界面模式:default (two columns), notebook, chat支持多…...
【Java SE】初遇Java,数据类型,运算符
🔥博客主页🔥:【 坊钰_CSDN博客 】 欢迎各位点赞👍评论✍收藏⭐ 1. Java 概述 1.1 Java 是什么 Java 是一种高级计算机语言,是一种可以编写跨平台应用软件,完全面向对象的程序设计语言。Java 语言简单易学…...
XSS(内含DVWA)
目录 一.XSS的攻击方式: 1. 反射型 XSS(Reflected XSS) 2. 存储型 XSS(Stored XSS) 3. DOM型 XSS(DOM-based XSS) 总结 二..XSS的危害 三.常见的XSS方式 1.script标签 四.常见基本过滤方…...
Alpamayo-R1-10B详细步骤:从supervisorctl服务管理到日志实时监控
Alpamayo-R1-10B详细步骤:从supervisorctl服务管理到日志实时监控 1. 引言:为什么你需要关注这个自动驾驶模型 如果你正在研究自动驾驶,或者对AI如何“看懂”路况并做出决策感到好奇,那么Alpamayo-R1-10B绝对值得你花时间了解。…...
POV-RAY入门指南 - 从零开始掌握光线追踪(1)
1. 初识POV-Ray:光线追踪的艺术 第一次打开POV-Ray时,我被它生成的金属球反射效果震撼到了——桌面上那个虚拟球体竟然能精确反射出周围环境的每处细节,连窗框的倒影都清晰可见。这种基于物理的光线追踪技术,正是好莱坞大片特效的…...
nlp_structbert_sentence-similarity_chinese-large部署教程:NVIDIA Container Toolkit深度配置
nlp_structbert_sentence-similarity_chinese-large部署教程:NVIDIA Container Toolkit深度配置 1. 项目概述 今天要介绍的是一个特别实用的中文语义相似度分析工具——基于StructBERT-Large模型开发的本地化解决方案。这个工具专门解决中文句子对的语义匹配问题&…...
从草图到文档:我用这5个Miro/PlantUML模板,高效搞定团队架构设计评审
从草图到文档:5个高效架构设计模板与团队协作实战指南 在敏捷开发环境中,架构设计往往陷入两难困境——既要快速响应需求变化,又要保证设计文档的准确性与可维护性。Tech Lead们经常面临这样的场景:在白板前与团队激情讨论出的架构…...
C#的readonly struct:不可变值类型的性能优势
在C#开发中,值类型的性能优化一直是开发者关注的焦点。readonly struct作为不可变值类型,不仅能够保证线程安全,还能带来显著的性能优势。本文将深入探讨readonly struct的设计原理及其在性能优化中的独特价值,帮助开发者更好地利…...
FastMoss TikTok电商数据爬取实战:JS逆向与MD5签名破解
1. FastMoss TikTok电商数据爬取的核心挑战 最近在研究FastMoss平台的TikTok电商数据爬取,发现最大的难点在于请求签名加密。当你访问https://www.fastmoss.com/zh/e-commerce/saleslist这个页面时,切换周榜会触发一个带有fm-sign签名的加密请求。这个签…...
python-langchain框架(1-9 返回字符串列表-格式解析器)
段代码演示了如何使用LangChain将大语言模型的自由文本输出转换为结构化的字符串列表。核心目标是让模型返回逗号分隔的多个值,并通过专用解析器自动拆分为Python列表。CommaSeparatedListOutputParser专用于解析逗号分隔的文本,自动处理空格、引号等边界…...
LangChain RAG实战:用PGVector把你的本地知识库变成智能问答机器人(Python代码详解)
LangChain RAG实战:用PGVector把你的本地知识库变成智能问答机器人(Python代码详解) 你是否曾经面对堆积如山的本地文档感到无从下手?PDF报告、Markdown笔记、TXT日志散落在各个文件夹,每次查找关键信息都像大海捞针。…...
专为AI打造的浏览器:内存占用仅为Chrome的1/9、比Chrome快11倍(Docker部署教程,支持飞牛nas等服务器部署)
文章目录 📖 介绍 📖 🏡 演示环境 🏡 📒 轻量级无头浏览器介绍与Docker部署指南 📒 📝 工具介绍 🎯 为什么选择它 🔧 Docker Compose 快速部署 💡 连接进行自动化操作 ⚠️ 注意事项 📊 性能对比 🎯 适用场景 ⚓️ 相关链接 ⚓️ 📖 介绍 📖 在自动…...
Win11下VMware保姆级安装指南:从许可证到CentOS镜像下载全流程
Win11下VMware与CentOS镜像高效部署实战手册 开篇:为什么选择VMwareCentOS组合? 刚接触虚拟化技术的开发者常面临一个关键抉择:如何在本地快速搭建稳定的Linux开发环境?VMware Workstation作为桌面虚拟化领域的标杆工具࿰…...
