Mapstruct应用
1.说明
MapStruct是一个代码生成器,它极大地简化了基于约定优于配置方法的Java bean类型之间映射的实现。
生成的映射代码使用普通的方法调用,因此快速、类型安全且易于理解。
2.应用案例
2.1搭建maven工程引入相关依赖
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.12</org.projectlombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!-- lombok dependencies should not end up on classpath -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- idea 2018.1.1 之前的版本需要添加下面的配置,后期的版本就不需要了,可以注释掉-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
关于lombok和mapstruct的版本兼容问题多说几句,maven插件要使用3.6.0版本以上、lombok使用1.16.16版本以上,另外编译的lombok mapstruct的插件不要忘了加上。否则会出现下面的错误:No property named “aaa” exists in source parameter(s). Did you mean “null”?
这种异常就是lombok编译异常导致缺少get setter方法造成的。还有就是缺少构造函数也会抛异常。
实体类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
private GenderEnum gender;
private Double height;
private LocalDateTime birthday;
}
枚举
public enum GenderEnum {
Male("1", "男"),
Female("0", "女");
private String code;
private String name;
public String getCode() {
return this.code;
}
public String getName() {
return this.name;
}
GenderEnum(String code, String name) {
this.code = code;
this.name = name;
}
}
VO类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {
private String name;
private int age;
private String gender;
private Double height;
private String birthday;
}
转换类
@Mapper
public interface StudentConvert {
StudentConvert INSTANCE = Mappers.getMapper(StudentConvert.class);
@Mapping(source = "gender.name", target = "gender")
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
StudentVO student2StudentVO(Student student);
}
测试类
public class Test {
public static void main(String[] args) {
Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(LocalDateTime.now()).build();
System.out.println(student);
// 这行代码便是实际要用的代码
StudentVO studentVO = StudentConvert.INSTANCE.student2StudentVO(student);
System.out.println(studentVO);
}
}
测试结果
在使用的过程中只需要短短的一行代码就可以实现类型转换,那么mapstruct帮我们做了什么,实现了类型的转换呢?来看一下编译后生成的接口实现类
public class StudentConvertImpl implements StudentConvert {
public StudentConvertImpl() {
}
public StudentVO student2StudentVO(Student student) {
if (student == null) {
return null;
} else {
StudentVO studentVO = new StudentVO();
//对枚举进行了转换,实体中的枚举值转换为了对应的中文描述
studentVO.setGender(this.studentGenderName(student));
// 对LocalDateTime对象按照指定格式进行了转换,同理Date也是一样
if (student.getBirthday() != null) {
studentVO.setBirthday(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(student.getBirthday()));
}
studentVO.setName(student.getName());
studentVO.setAge(student.getAge());
studentVO.setHeight(student.getHeight());
return studentVO;
}
}
private String studentGenderName(Student student) {
if (student == null) {
return null;
} else {
GenderEnum gender = student.getGender();
if (gender == null) {
return null;
} else {
String name = gender.getName();
return name == null ? null : name;
}
}
}
}
2.2List转换
属性映射基于上面的mapping配置
转换类
添加students2StudentVOs
方法
public interface StudentConvert {
StudentConvert INSTANCE = Mappers.getMapper(StudentConvert.class);
@Mapping(source = "gender.name", target = "gender")
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
StudentVO student2StudentVO(Student student);
List<StudentVO> students2StudentVOs(List<Student> studentList);
}
测试类
public class Test {
public static void main(String[] args) {
Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(LocalDateTime.now()).build();
List<Student> list = new ArrayList<>();
list.add(student);
List<StudentVO> result = StudentConvert.INSTANCE.students2StudentVOs(list);
System.out.println(result);
}
}
测试结果
2.3 多对象转换到一个对象
实体类
- 课程
@Data
@AllArgsConstructor
@Builder
@NoArgsConstructor
public class Course {
private String courseName;
private int sortNo;
private long id;
}
- 学生
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
private GenderEnum gender;
private Double height;
private LocalDateTime birthday;
}
VO类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {
private String name;
private int age;
private String gender;
private Double height;
private String birthday;
private String course;
}
转换类
@Mapper
public interface StudentConvert {
StudentConvert INSTANCE = Mappers.getMapper(StudentConvert.class);
@Mapping(source = "student.gender.name", target = "gender")
@Mapping(source = "student.birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(source = "course.courseName", target = "course")
StudentVO studentAndCourse2StudentVO(Student student, Course course);
List<StudentVO> students2StudentVOs(List<Student> studentList);
}
测试类
public class Test {
public static void main(String[] args) {
Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(LocalDateTime.now()).build();
Course course = Course.builder().id(1L).courseName("语文").build();
StudentVO studentVO = StudentConvert.INSTANCE.studentAndCourse2StudentVO(student, course);
System.out.println(studentVO);
List<Student> list = new ArrayList<>();
list.add(student);
List<StudentVO> result = StudentConvert.INSTANCE.students2StudentVOs(list);
System.out.println(result);
}
}
测试结果
2.4默认值
转换类
@Mapper
public interface StudentConvert {
StudentConvert INSTANCE = Mappers.getMapper(StudentConvert.class);
@Mapping(source = "student.gender.name", target = "gender")
@Mapping(source = "student.birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(source = "course.courseName", target = "course")
@Mapping(target = "name", source = "student.name", defaultValue = "张三")
StudentVO studentAndCourse2StudentVO(Student student, Course course);
List<StudentVO> students2StudentVOs(List<Student> studentList);
}
测试类
public class Test {
public static void main(String[] args) {
Student student = Student.builder().age(6).gender(GenderEnum.Male).height(121.1).birthday(LocalDateTime.now()).build();
Course course = Course.builder().id(1L).courseName("语文").build();
StudentVO studentVO = StudentConvert.INSTANCE.studentAndCourse2StudentVO(student, course);
System.out.println(studentVO);
List<Student> list = new ArrayList<>();
list.add(student);
List<StudentVO> result = StudentConvert.INSTANCE.students2StudentVOs(list);
System.out.println(result);
}
}
可以看到默认值对于集合的转换并不会生效
编译后的接口实现类
public class StudentConvertImpl implements StudentConvert {
public StudentConvertImpl() {
}
public StudentVO studentAndCourse2StudentVO(Student student, Course course) {
if (student == null && course == null) {
return null;
} else {
StudentVO.StudentVOBuilder studentVO = StudentVO.builder();
if (student != null) {
studentVO.gender(this.studentGenderName(student));
if (student.getBirthday() != null) {
studentVO.birthday(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(student.getBirthday()));
}
if (student.getName() != null) {
studentVO.name(student.getName());
} else {
studentVO.name("张三");
}
studentVO.age(student.getAge());
studentVO.height(student.getHeight());
}
if (course != null) {
studentVO.course(course.getCourseName());
}
return studentVO.build();
}
}
public List<StudentVO> students2StudentVOs(List<Student> studentList) {
if (studentList == null) {
return null;
} else {
List<StudentVO> list = new ArrayList(studentList.size());
Iterator var3 = studentList.iterator();
while(var3.hasNext()) {
Student student = (Student)var3.next();
list.add(this.studentToStudentVO(student));
}
return list;
}
}
private String studentGenderName(Student student) {
if (student == null) {
return null;
} else {
GenderEnum gender = student.getGender();
if (gender == null) {
return null;
} else {
String name = gender.getName();
return name == null ? null : name;
}
}
}
protected StudentVO studentToStudentVO(Student student) {
if (student == null) {
return null;
} else {
StudentVO.StudentVOBuilder studentVO = StudentVO.builder();
studentVO.name(student.getName());
studentVO.age(student.getAge());
if (student.getGender() != null) {
studentVO.gender(student.getGender().name());
}
studentVO.height(student.getHeight());
if (student.getBirthday() != null) {
studentVO.birthday(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(student.getBirthday()));
}
return studentVO.build();
}
}
}