【springboot进阶】优雅使用 MapStruct 进行类复制
创始人
2024-04-08 04:03:10
0次

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

 一、MapStruct 介绍

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

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

二、MapStruct 配置

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

org.mapstructmapstruct1.5.0.Finalprovided
org.mapstructmapstruct-processor1.5.0.Finalprovided

三、MapStruct 使用

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

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

/*** 

* 学生表*

** @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 {@ResourceStudentService 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 page() {List students = studentService.list();List 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的东西后,出现奇奇怪怪的情况,一种重新编译一下项目就能解决。

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...