【springboot进阶】优雅使用 MapStruct 进行类复制

news2025/7/12 8:08:54

项目中经常会遇到这样的一个情况:从数据库读取到数据,并不是直接返回给前端做展示的,还需要字段的加工,例如记录的时间戳是不需要的、一些敏感数据更是不能等等。传统的做法就是创建一个新的类,然后写一堆的get/set方法进行赋值,如果字段很多的话,那简直是噩梦,有时候还担心会漏掉等等。

 一、MapStruct 介绍

MapStruct 简单来说就是一个属性映射工具,主要用于解决数据模型之间不通用的情况。这里主要说一下它的优点在于性能好,像笔者在未接触 MapStruct 以前一直使用的 BeanUtils 工具进行转换,当时也知道这样性能不好,但是为了能偷懒,所以...

其实 MapStruct 也不是有神秘之处,其实原理在于,Java程序执行的过程,是由编译器先把java文件编译成class字节码文件,然后由JVM去解释执行class文件。Mapstruct正是在java文件到class这一步帮我们实现了转换方法,即做了预处理,提前编译好文件。

二、MapStruct 配置

需要引入 mapstruct 和 mapstruct-processor,同时 scope 设置为 provided ,即它只影响到编译,测试阶段。

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.0.Final</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.0.Final</version>
    <scope>provided</scope>
</dependency>

三、MapStruct 使用

这边演示的是一般项目中,从数据库读取到数据,到返回前端展示的过程。

 假设我们有一个student表,实体字段信息如下。

/**
 * <p>
 * 学生表
 * </p>
 *
 * @author Liurb
 * @since 2022-11-13
 */
@Getter
@Setter
@TableName("demo_student")
public class Student implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 学生名称
     */
    @TableField("`name`")
    private String name;

    /**
     * 学生年龄
     */
    @TableField("age")
    private Integer age;

    /**
     * 学生性别
     */
    @TableField("sex")
    private String sex;

    /**
     * 创建时间
     */
    @TableField("created_at")
    private LocalDateTime createdAt;


}

但是前端页面展示的时候,某些字段需要调整。例如,学生信息需要展示在首页和列表页,他们的数据模型字段名称是不一致的。

学生首页展示vo 需要调整学生的 id 为 userId, 学生名称为 userName 。

/**
 * 学生首页展示vo
 *
 *
 * @Author LiuRuiBin
 * @Date 2022/11/13
 */
@Data
public class StudentHomeVo {

    private Integer userId;

    private String userName;

    private Integer age;

    private String sex;

}

学生分页展示vo 需要调整学生的性别为 gender 。 

/**
 * 学生分页展示vo
 *
 *
 * @Author LiuRuiBin
 * @Date 2022/11/13
 */
@Data
public class StudentPageVo {

    private Integer id;

    private String name;

    private Integer age;

    private String gender;

}

创建 学生实体的mapper,由于要区分 mybatis-plus 的底层mapper,所以这里的命名以 StructMapper 结尾,尽量避免重名的情况。所以注意 @Mapper 注解也要使用 org.mapstruct 包下的。

/**
 * 学生实体转换接口
 *
 * 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
 *
 * @Author LiuRuiBin
 * @Date 2022/11/13
 */
@Mapper
public interface StudentStructMapper {

    /**
     * 获取该类自动生成的实现类的实例
     *
     */
    StudentStructMapper INSTANCES = Mappers.getMapper(StudentStructMapper.class);

    /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 注解 用于定义属性复制规则
     * source 指定源对象属性
     * target指定目标对象属性
     *
     * @param student 这个参数就是源对象,也就是需要被复制的对象
     * @return 返回的是目标对象,就是最终的结果对象
     */
    @Mappings({
            @Mapping(source = "id", target = "userId"),
            @Mapping(source = "name", target = "userName")
    })
    StudentHomeVo toStudentHomeVo(Student student);

    /**
     * 也可以实现多个复制方法,一般将一个实体源对象的转换写在一起
     *
     * @param student
     * @return
     */
    @Mapping(source = "sex", target = "gender")
    StudentPageVo toStudentPageVo(Student student);
}

四、测试

我们创建一个controller,模拟一般项目的接口请求。

/**
 * mapstruct实例控制器
 *
 * @Author LiuRuiBin
 * @Date 2022/11/13
 */
@RestController
@RequestMapping("/demo_api/mapstruct")
public class MapStructController {

    @Resource
    StudentService studentService;

    @GetMapping("/home/{id}")
    public StudentHomeVo home(@PathVariable("id")Integer id) {

        Student student = studentService.getById(id);

        StudentHomeVo studentHomeVo = StudentStructMapper.INSTANCES.toStudentHomeVo(student);

        return studentHomeVo;
    }

    @GetMapping("/page")
    public List<StudentPageVo> page() {
        List<Student> students = studentService.list();

        List<StudentPageVo> studentPageVos = students.stream().map(item -> {

            StudentPageVo studentPageVo = StudentStructMapper.INSTANCES.toStudentPageVo(item);
            return studentPageVo;
        }).collect(Collectors.toList());

        return studentPageVos;
    }

}

数据表的记录如下

调用首页展示接口的情况如下,可以看到,返回的新字段已经成功赋值。

 接下来,看一下分页的数据,新字段 性别 gender 也同样赋值成功。

 五、遇到的坑

1、java.lang.NoSuchMethodError

如果现在我们将学生首页vo类的 age 字段,调整为 userAge,运行项目,在请求一次接口,你会发现这时候会报错,提示找不到 setAge 方法。

为什么会这样呢?其实原因在于上面说的 MapStruct 工作原理,这时候查看转换接口的实现就可以知道是什么情况了。

 实现类还是调用的 setAge 方法进行赋值,但是我们的 StudentHomeVo 已经被我们改过,没有这个方法了,所以运行时候就会报错了。

那么这种情况如何解决了,其实也很简单,重新编译一次项目就可以了。

 重新编译运行,再请求一次接口,可以看到成功返回,并且新字段也有赋值。 

如果发现调整了字段,或者改过转换mapper的东西后,出现奇奇怪怪的情况,一种重新编译一下项目就能解决。

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

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

相关文章

数据结构【队列】

文章目录&#xff08;一&#xff09;队列定义&#xff08;二&#xff09;队列实现&#xff08;1&#xff09;创建结构体&#xff08;2&#xff09;具体函数实现及解析1.1 初始化队列1.2入队列1.3出队列1.4取队首元素1.5取队尾元素1.6返回队列个数1.7判断是否为空1.8销毁队列&am…

springCloud的 consul的下载与安装

下载地址&#xff1a;Install | Consul | HashiCorp Developer 下载自己需要使用的版本 下载后会有一个exe 文件通过cmd 命令行来执行这个exe 文件consul agent -dev -client0.0.0.0 出现此页面后执行8500 端口 请求地址&#xff1a;http://127.0.0.1:8500/ 出现此页面说明启…

黑苹果入门:必备工具篇

以下给大家汇总的这些软件工具都是我们在安装使用黑苹果过程中可能会用到的&#xff0c;至于使用方法&#xff0c;在这里我就不做过多介绍了。 本次只提供软件下载地址&#xff0c;不提供使用方法&#xff0c;不知道如何使用软件工具的童鞋&#xff0c;可以在百度翻翻相关教程…

第5章 C语言高级的库函数

文章目录文档配套视频讲解链接地址第05章 C库函数5.1 assert.h 断言库5.2 ctype.h 测试和映射字符5.3 math.h 数学库5.4 stdlib.h 标准库1. 字符串转整数、浮点数2. strtod 把字符串中的数字转换成浮点数并返回数字的下一个字符的位置3. strtol 字符串转整数4. strtoul 字符串转…

vue3 antd多级动态菜单(二)后台管理系统(两种方法过滤有无子菜单children)

vue3 antd 多级动态菜单&#xff08;精修版本&#xff09; 两种方法实现对children的筛选相关文章推送&#xff08;供参考&#xff09;场景复现实现效果解决方法hasChildren与noChilren函数过滤v-if v-else判断有无children【推荐】&#x1f525;两种方法公用代码sunmmary下期预…

ESP32 入门笔记06: WIFI时钟 + FreeRTOS+《两只老虎》 (ESP32 for Arduino IDE)

ESP32FreeRTOS Esp32 模块中已经提供了 FreeRTOS&#xff08;实时操作系统&#xff09;固件。 FreeRTOS有助于提高系统性能和管理模块的资源。FreeRTOS允许用户处理多项任务&#xff0c;如测量传感器读数&#xff0c;发出网络请求&#xff0c;控制电机速度等&#xff0c;所有…

靶向肿瘤代谢,助力攻克癌症

肿瘤代谢的简介 肿瘤代谢的起源在于奥托沃伯格 (Otto Warburg) 的假设&#xff0c;他也因发现线粒体呼吸链复合物 IV 而获得 1931 年诺贝尔生理学或医学奖。Warburg 观察到&#xff0c;与正常组织相比&#xff0c;体外癌组织切片使用大量葡萄糖生成乳酸 (即使在有氧的情况下也是…

SBT10100VDC-ASEMI低压降贴片肖特基二极管SBT10100VDC

编辑-Z SBT10100VDC在TO-263封装里采用的2个芯片&#xff0c;其尺寸都是62MIL&#xff0c;是一款低压降贴片肖特基二极管。SBT10100VDC的浪涌电流Ifsm为150A&#xff0c;漏电流(Ir)为4uA&#xff0c;其工作时耐温度范围为-65~150摄氏度。SBT10100VDC采用金属硅芯片材质&#x…

QScintilla代码跳转时indicator工作不正确的问题

首先看我这几个文章&#xff0c;知道一下indicator是什么&#xff0c;以及上下文&#xff1a; https://biao2488890051.blog.csdn.net/article/details/126798996?spm1001.2014.3001.5502 目标&#xff1a; 我现在要做按住 ctrl 鼠标左键点击释放 发生函数/变量的 定义/声明…

Pandas中你一定要掌握的时间序列相关高级功能

&#x1f4a1; 作者&#xff1a;韩信子ShowMeAI &#x1f4d8; 数据分析实战系列&#xff1a;https://www.showmeai.tech/tutorials/40 &#x1f4d8; 本文地址&#xff1a;https://www.showmeai.tech/article-detail/389 &#x1f4e2; 声明&#xff1a;版权所有&#xff0c;转…

ceph浅谈

总谈 ceph简介 用上ceph&#xff0c;多台机器的磁盘空间在一起了&#xff0c;在一台机器上就可以看到使用所有空间。 还可以保存多份安全备份 存储先ceph&#xff0c;自我管理修复&#xff0c;跨机房&#xff0c;节点越多&#xff0c;并行化&#xff0c;论上&#xff0c;节点越…

【虚幻引擎UE】UE5 实现相机录制视频并导出(C++调用外部exe)

说明: 该功能暂不支持导出声音。 由于OpenCV3和UE5不太兼容,因此考虑制作外部exe实现视频合成。 一、创建渲染目标 二、创建Actor加场景捕获组件2D 三、创建UE5内的C++代码 1、实现 SavePicToFile 导出图片蓝图函数 .cpp文件 // Fill out your copyright notice in the De…

数字集成电路设计(二、Verilog HDL基础知识)

文章目录1. 语言要素1.1 空白符1.2 注释符1.3 标识符1.3.1 转义标识符1.4 关键字1.5 数值1.5.1 整数及其表示方式1.5.2 实数及其表示方式1.5.3 字符串及其表示方式2. 数据类型2.1 物理数据类型2.1.1 连线型2.1.2 寄存器型2.2 连线型和寄存器型数据类型的声明2.2.1 连线型数据类…

深入了解海豚调度DolphinScheduler

深入了解海豚调度DolphinScheduler一、海豚调度介绍二、海豚调度特性三、建议配置四、名词解释五、模块介绍六、功能介绍1.项目首页2.工作流定义3.工作流实例4.任务实例5.任务定义七、任务类型1.SQL2.SPARK节点3.Apache Zeppelin八、集群部署1.前置准备工作2.准备 DolphinSched…

实现注册与登录模块

目录 1、加载依赖 2、实现jwt工具类jwtUtil类 3、实现config.filter.JwtAuthenticationTokenFilter类 4、配置config.SecurityConfig类 5、创建后端api之前对数据库进行修改 6、写API一共需要的三个地方 7、实现三个接口 8、验证用户登录用API调试 9、https://jwt.io/解…

MySQL表的增删查改(CRUD)

文章目录前言一、新增数据二、查询数据全列查询指定列查询表达式查询指定别名查询去重查询排序查询条件查询分页查询三、修改数据四、删除数据前言 CRUD代表: 增加(create) ,查询(retrieve) ,更新(update) ,删除(delete) 单词首字母。 一、新增数据 SQL使用insert关键字来表…

二叉搜索树、红黑树详解、红黑树高的应用、TreeMap的应用(图文详解)-Kotlin版本代码

二叉搜索树 何为二叉搜索树&#xff1f; 二叉搜索树是一种特殊的二叉树&#xff0c;它的左子节点总是小于或等于根节点&#xff0c;而右子节点 总是大于或等于根节点。 如下图&#xff0c;即是一颗二叉搜索树。 对于二叉搜索树来说&#xff0c;中序遍历可以遍历按照节点值…

【JavaSE】重载和重写

前言&#xff1a; 作者简介&#xff1a;爱吃大白菜1132 人生格言:纸上得来终觉浅&#xff0c;绝知此事要躬行 如果文章知识点有错误的地方不吝赐教&#xff0c;和大家一起学习&#xff0c;一起进步&#xff01; 如果觉得博主文章还不错的话&#xff0c;希望三连支持&#xff01…

python--敲击木鱼积累功德小项目(更新版(2))

前言&#xff1a;前几天上课闲着没事写了一个python敲击木鱼积累功德的小项目&#xff0c;当时纯粹就是写着玩&#xff0c;回顾一下鼠标事件的东西还记不记得&#xff0c;发现这个博客的点赞和收藏量还挺高的&#xff0c;我当时也没有把它当回事&#xff0c;后面也有很多人问怎…

11.11一些资源整理和总结

使用python读取tensorboard文件中的数据并写入到excel当中去能够代替Originlab的画图软件&#xff08;macos&#xff09;Mac款origin来了&#xff01;还不来看看&#xff01;ptflops&#xff1a;计算网络参数FLOPs的工具[github] 4 中方式计算 FLOPs&#xff08;知乎&#xff0…