一、前言
我们在开发过程中经常会有对象属性之间相互转换的情况,例如接口请求vo需要映射到数据库实体。通常做法是通过get/set方法进行映射,但是属性过多时显得代码冗长。
还有做法是通过Spring或者Apache给我们提供的BeatUtils工具,但其实BeanUtils 就是一个大老粗,只能同属性映射,或者在属性相同的情况下,允许被映射的对象属性少;但当遇到被映射的属性数据类型被修改或者被映射的字段名被修改,则会导致映射失败。使用BeanUtils也有一些注意,他的实现机制是通过反射,导致拷贝属性的花费时间较长,性能很低。“阿里巴巴编码规范”也有提尽量避免使用BeanUtils工具。
经过了解使用MapStruct。
MapStruct很优雅的帮我们解决了这个问题。
二、MapStrut使用方式
1、引入pom依赖
<!-- mapStruct开始 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</dependency>
<!-- mapStruct结束 -->
2、准备实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Serializable {
/**
* id
*/
private Integer id;
/**
* 学生姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private String sex;
}
3、准备一个vo
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentRequestVo {
/**
* id
*/
private Integer studentId;
/**
* 学生姓名
*/
private String studentName;
/**
* 年龄
*/
private Integer studentAge;
}
4、转换
4.1、对象转换
4.1.1、定义映射器
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface StudentMapStrut {
@Mappings({
@Mapping(target = "id",source = "studentId"),
@Mapping(target = "name",source = "studentName"),
@Mapping(target = "age",source = "studentAge")
})
Student studentConvert(StudentRequestVo studentRequestVo);
}
4.1.2、测试效果
@SpringBootTest(classes = DemoApplication.class)
@RunWith(SpringRunner.class)
public class CommonTest {
@Autowired
private StudentMapStrut studentMapStrut;
@Test
public void testMapStrut(){
Student student = studentMapStrut.studentConvert(new StudentRequestVo(1, "张三", 15));
System.out.println(student);
}
}
打印效果如下:
如上图,名字不同且没有指定映射关系的会被设置为null
4.2 List互转
4.2.1 定义映射器
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface StudentMapStrut {
@Mappings({
@Mapping(target = "id",source = "studentId"),
@Mapping(target = "name",source = "studentName"),
@Mapping(target = "age",source = "studentAge")
})
Student studentConvert(StudentRequestVo studentRequestVo);
List<Student> studentListConvert(List<StudentRequestVo> studentRequestVos);
}
4.2.2 测试效果
@Test
public void testMapStrut(){
StudentRequestVo studentRequestVo1 = new StudentRequestVo(1, "张三", 15);
StudentRequestVo studentRequestVo2 = new StudentRequestVo(2, "李四", 16);
ArrayList<StudentRequestVo> studentRequestVos = new ArrayList<>();
studentRequestVos.add(studentRequestVo1);
studentRequestVos.add(studentRequestVo2);
List<Student> students = studentMapStrut.studentListConvert(studentRequestVos);
System.err.println(students);
}
4.3 多个对象映射一个对象
实体:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentAndTeacher implements Serializable {
/**
* id
*/
private Integer id;
/**
* 学生姓名
*/
private String studentName;
/**
* 老师姓名
*/
private String teacherName;
}
学生vo:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentRequestVo {
/**
* id
*/
private Integer studentId;
/**
* 学生姓名
*/
private String studentName;
/**
* 年龄
*/
private Integer studentAge;
}
老师vo:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TeacherRequestVo {
/**
* id
*/
private Integer teacherId;
/**
* 学生姓名
*/
private String teacherName;
/**
* 年龄
*/
private Integer teacherAge;
}
4.3.1、定义映射器
@Mappings({
@Mapping(target = "studentName",source = "studentRequestVo.studentName"),
@Mapping(target = "teacherName",source = "teacherRequestVo.teacherName")
})
StudentAndTeacher studentAndTeacherConvert(StudentRequestVo studentRequestVo,TeacherRequestVo teacherRequestVo);
4.3.2、测试效果
@Test
public void testMapStrut(){
StudentRequestVo studentRequestVo = new StudentRequestVo(1, "张三", 15);
TeacherRequestVo teacherRequestVo = new TeacherRequestVo(1, "老师", 25);
StudentAndTeacher studentAndTeacher = studentMapStrut.studentAndTeacherConvert(studentRequestVo, teacherRequestVo);
System.err.println(studentAndTeacher);
}
5、自定义转换
以对象转换为例,我们想把年龄20岁以下的,统一存储为0,20岁以上的统一存储为1.
5.1定义映射器
@Mappings({
@Mapping(target = "id",source = "studentId"),
@Mapping(target = "name",source = "studentName"),
@Mapping(target = "age",expression = "java(com.example.demo.mapstrut.MapStructUtil.ageConvert(studentRequestVo.getStudentAge()))")
})
Student studentConvert(StudentRequestVo studentRequestVo);
5.2 定义转换类
public class MapStructUtil {
public static Integer ageConvert(Integer studentAge){
if (studentAge<=20){
return 0;
}else {
return 1;
}
}
}
5.3 测试效果
@Test
public void testMapStrut(){
StudentRequestVo studentRequestVo = new StudentRequestVo(1, "张三", 15);
Student student = studentMapStrut.studentConvert(studentRequestVo);
System.err.println(student);
}
三.总结:
mapstruct还有很多功能,例如dateFormat 、numberFormat 实现自定义转换,平时使用基本可以参考本文。如需更详细了解可参考官网: https://mapstruct.org/documentation/stable/reference/html/