目录
项目中经常会遇到这样的一个情况:从数据库读取到数据,并不是直接返回给前端做展示的,还需要字段的加工,例如记录的时间戳是不需要的、一些敏感数据更是不能等等。传统的做法就是创建一个新的类,然后写一堆的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 Liurb
* @Date 2022/11/13
*/
@Data
public class StudentHomeVo {
private Integer userId;
private String userName;
private Integer age;
private String sex;
}
学生分页展示vo 需要调整学生的性别为 gender 。
/**
* 学生分页展示vo
*
*
* @Author Liurb
* @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 Liurb
* @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 Liurb
* @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的东西后,出现奇奇怪怪的情况,一种重新编译一下项目就能解决。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/93596.html