优雅使用 MapStruct 进行类复制

news2024/9/28 23:35:00

前言

在项目中,常常会遇到从数据库读取数据后不能直接返回给前端展示的情况,因为还需要对字段进行加工,比如去除时间戳记录、隐藏敏感数据等。传统的处理方式是创建一个新类,然后编写大量的 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等注解)生成具体的映射实现类。这个生成过程是在编译时进行的,生成的代码会被编译到最终的字节码中。
  • 例如,如果有一个定义了从SourceObjectTargetObject映射关系的@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 {

    @Test
    public 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());
    }
}

简单看一下生成的代码

UserMapperImpl.java

 // 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;
      }
   }
}

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

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

相关文章

讯飞星火编排创建智能体学习(二)决策节点

目录 概述 决策节点 文生图节点 连接节点 测试结果 概述 在上一篇博文讯飞星火编排创建智能体学习&#xff08;一&#xff09;最简单的智能体构建-CSDN博客&#xff0c;我介绍了编排创作智能体&#xff0c;这篇来介绍一下“决策节点”。 决策节点 在编排创作智能体中&…

YOLOv8最新改进2023 CVPR 结合BiFormer

1,原理部分 作为视觉转换器的核心构建块,衰减是捕获长距离依赖性的强大工具。然而,这种能力是有代价的:它会产生巨大的计算负担和沉重的内存占用,因为所有空间位置的成对标记交互都是计算的。一系列作品试图通过将手工制作和与内容无关的稀疏性引入 attention 来缓解这个问…

一文上手skywalking【上】

一、skywalking预览 1.1 skywalking 概述 ​ Apache SkyWalking, 适用于分布式系统的应用程序性能监控工具&#xff0c;专为微服务、云原生和基于容器的 &#xff08;Kubernetes&#xff09; 架构而设计。官方地址: https://skywalking.apache.org/ 适用于分布式系统的应用程…

从密码学看盲拍合约:智能合约的隐私与安全新革命!

文章目录 前言一、什么是盲拍合约&#xff1f;二、盲拍合约的优势1.时间压力的缓解2.绑定与秘密的挑战 三、盲拍合约的工作原理1.提交盲出价2.披露出价3.结束拍卖4.退款机制 四、代码示例总结 前言 随着区块链技术的发展&#xff0c;智能合约在各种场景中的应用越来越广泛。盲…

番外篇 | 复现AC-YOLOv5,进行自动化织物缺陷检测

前言:Hello大家好,我是小哥谈。我们提出了一种基于AC-YOLOv5的新型纺织缺陷检测方法。将空洞空间金字塔池化(ASPP)模块引入YOLOv5主干网络中,提出了squeeze-and-excitation(CSE)通道注意力模块,并将其引入到YOLOv5主干网络中。🌈 目录 🚀1.基础概念 🚀2.添…

【d54_2】【Java】【力扣】142.环形链表

思路 关于判断是否重复的就hashSet&#xff0c;这种有主动去重性质的类 新建一个hashSet 遍历链表并放进hashSet&#xff0c; 如果不能放&#xff0c;说明这个遍历过&#xff0c;这个就是环的地方 如果最后到遍历到null&#xff0c;说明没环 代码 /*** Definition for s…

5.3 克拉默法则、逆矩阵和体积

本节是使用代数而不是消元法来求解 A x b A\boldsymbol x\boldsymbol b Axb 和 A − 1 A^{-1} A−1。所有的公式都会除以 det ⁡ A \det A detA&#xff0c; A − 1 A^{-1} A−1 和 A − 1 b A^{-1}\boldsymbol b A−1b 中的每个元素都是一个行列式除以 A A A 的行列式。…

基于微信小程序的网上商城+ssm(lw+演示+源码+运行)

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;微信小程序被用户普遍使用&#xff0c;为方便用户能够可以…

STM32F407单片机编程入门(二十七)以太网接口详解及实战含源码

文章目录 一.概要二.单片机以太网系统基本结构1.OSI 七层模型2.单片机实现以太网功能组成 三.STM32F407VET6单片机以太网内部结构1.MII接口介绍2.RMII接口介绍 四.LWIP TCP/IP协议栈介绍五.PHY收发器LAN8720介绍1.LAN8720内部框图2.LAN8720应用电路3.LAN8720以太网模块 六.Cube…

在公司网络环境下,无法访问公共网络时,可在插件端配置网络代理后使用通义灵码

在公司网络环境下&#xff0c;无法访问公共网络时&#xff0c;可在插件端配置网络代理后使用通义灵码。 通义灵码插件下载&#xff1a;通义灵码_智能编码助手_AI编程-阿里云 配置网络代理 公司网络通常使用 HTTP 代理服务器在网络流量发送到目标位置之前进行拦截&#xff0c;以…

6--苍穹外卖-SpringBoot项目中菜品管理 详解(二)

目录 菜品分页查询 需求分析和设计 代码开发 设计DTO类 设计VO类 Controller层 Service层接口 Service层实现类 Mapper层 功能测试 删除菜品 需求设计和分析 代码开发 Controller层 Service层接口 Service层实现类 Mapper层 功能测试 修改菜品 需求分析和设…

Spring--boot自动配置原理案例--阿里云--starter

Spring–boot自动配置原理案例–阿里云–starter 定义这个starter的作用是它可以将阿里云的工具类自动放入IOC容器中&#xff0c;供人使用。 我们看一看构建starter的过程&#xff0c;其实就是在atuoconfigure模块中加入工具类&#xff0c;然后写一个配置类在其中将工具类放入…

【ChromeDriver安装】爬虫必备

以下是安装和配置 chromedriver 的步骤&#xff1a; 1. 确认 Chrome 浏览器版本 打开 Chrome 浏览器&#xff0c;点击右上角的菜单按钮&#xff08;三个点&#xff09;&#xff0c;选择“帮助” > “关于 Google Chrome”。 2. 下载 Chromedriver 根据你的 Chrome 版本&…

【研赛A题成品论文】24华为杯数学建模研赛A题成品论文+可运行代码丨免费分享

2024华为杯研究生数学建模竞赛A题精品成品论文已出&#xff01; A题 风电场有功功率优化分配 一、问题分析 A题是一道工程建模与优化类问题&#xff0c;其目的是根据题目所给的附件数据资料分析风机主轴及塔架疲劳损伤程度&#xff0c;以及建立优化模型求解最优有功功率分配…

哪些AI软件能轻松搞定你的文案、总结、论文、计划书?

大家好&#xff01;在我们每天紧张忙碌的生活中&#xff0c;有时候一天结束时&#xff0c;我们还有一堆事情等着处理。 图片 但别担心&#xff0c;今天我要为大家介绍几款AI软件&#xff0c;它们可以在你忙碌的一天结束后&#xff0c;成为你的得力助手&#xff0c;帮你轻松管…

初识Tomcat

Tomcat是一款可以运行javaWebAPP的服务器软件。 一个服务器想要执行java代码&#xff0c;则需要JRE&#xff08;jvm、java运行环境等&#xff09;&#xff0c;但是需要执行javaWEB项目则还需要服务器软件&#xff0c;Tomacat就是其中很流行的一款。因为一个javaWEB项目会有很多…

Accelerate单卡,多卡config文件配置

依赖库 from accelerate import Accelerator from accelerate import DistributedDataParallelKwargs ddp_kwargs DistributedDataParallelKwargs(find_unused_parametersTrue) accelerator Accelerator(kwargs_handlers[ddp_kwargs]) 代码中删除所有的.cuda() 或者to(devic…

Xshell连接服务器

一、Xshell-7.0.0164p、Xftp 7下载 1.1、文件下载 通过网盘分享的文件&#xff1a;xshell 链接: https://pan.baidu.com/s/1qc0CPv4Hkl19hI9tyvYZkQ 提取码: 5snq –来自百度网盘超级会员v2的分享 1.2、ip连接 下shell和xftp操作一样&#xff1a;找到文件—》新建—》名称随…

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,1-1

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…

论文不同写作风格下的ChatGPT提示词分享

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 在学术论文写作中&#xff0c;不同的写作风格能显著影响文章的表达效果与读者的理解。无论是描述性、分析性、论证性&#xff0c;还是批判性写作风格&#xff0c;合理选择和运用恰当的写…