MapStruct是什么
MapStruct is a code generator that greatly simplifies the implementation of mappings between Java bean types based on a convention over configuration approach.
MapStruct是一个极大简化javaBean之间互相转换的代码生成器,即简化实体类DTO的互相转化,官方文档
maven依赖
<properties>
<org.mapstruct.version>1.3.0.Final</org.mapstruct.version>
</properties>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<scope>provided</scope>
</dependency>
集成MapStruct官方提供了两种方式,
上面配置文件内我们采用的是直接添加Maven依赖,而官方文档还提供了另外一种方式,采用Maven插件形式配置,配置如下所示:
<properties>
<org.mapstruct.version>1.3.0.Final</org.mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version> <!-- or newer version -->
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
一对一转换
//实体类
public class Customer {
private Long id;
private String name;
//getters and setter omitted for brevity
}
//DTO
public class CustomerDto {
public Long id;
public String customerName;
}
//(org.mapstruct.Mapper) annotation
@Mapper
public interface CustomerMapper {
CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class );
//相同属性不需要用@Mapping指定,如Customer和CustomerDto中的id
//customerName与name不同,则必须指定
@Mapping(source = "customerName", target = "name")
Customer toCustomer(CustomerDto customerDto);
@InheritInverseConfiguration
CustomerDto fromCustomer(Customer customer);
}
可以看到CustomerMapper是一个接口的形式存在的,当然也可以是一个抽象类,如果需要在转换的时候才用个性化的定制
的时候可以采用抽象类的方式,相应的代码配置官方文档已经声明。@Mapper注解是用于标注接口、抽象类是被MapStruct自动映射的
标识,只有存在该注解才会将内部的接口方法自动实现。
使用方式1:不需要做过多的配置内容,获取Mapper的方式就是采用Mappers通过动态工厂内部反射机制完成Mapper实现类的获取。
public class MapStructTest {
@Test
public void directMapping() {
Customer customer = new Customer();
customer.setId(12L);
customer.setName("customer001");
CustomerDto customerDto = CustomerConverter.INSTANCE.fromCustomer(customer);
System.out.println("customerDto = " + customerDto);
}
}
使用方式2:使用springBoot依赖注入
// Specifies the component model to which the generated mapper should adhere. Supported values are
//指定mapper的注入模板 :cdi spring jsr330
//@mapper添加参数 componentModel = "spring"
@Mapper(componentModel = "spring")
public interface CustomerConverter {
CustomerConverter INSTANCE = Mappers.getMapper(CustomerConverter.class);
@Mapping(source = "customerName", target = "name")
Customer toCustomer(CustomerDto customerDto);
@InheritInverseConfiguration
CustomerDto fromCustomer(Customer customer);
}
public class MapStructTest {
//使用autowired自动注入
@Autowired
private CustomerConverter customerConverter;
@Test
public void directMapping() {
Customer customer = new Customer();
customer.setId(12L);
customer.setName("customer001");
// CustomerDto customerDto = CustomerConverter.INSTANCE.fromCustomer(customer);
CustomerDto customerDto = customerConverter.fromCustomer(customer);
System.out.println("customerDto = " + customerDto);
}
}
结果: customerDto = CustomerDto(id=12, customerName=customer001)
IDEA插件: MapStruct Support
安装idea插件MapStruct Support在编写mapper时可以提示不相同的属性映射
@Mapper(componentModel = "spring")
public interface CustomerConverter {
CustomerConverter INSTANCE = Mappers.getMapper(CustomerConverter.class);
@Mapping(source = "customerName", target = "name") //插件可以提示source和target参数
Customer toCustomer(CustomerDto customerDto);
@InheritInverseConfiguration
CustomerDto fromCustomer(Customer customer);
}
实体类中有List属性的转换
@Data
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
}
@Data
public class Teacher {
private List<Student> students;
}
@Data
public class StudentDto {
private Integer id;
private String name;
private Integer age;
private String gender;
}
@Data
public class TeacherDto {
private List<StudentDto> studentDtos;
}
要将teacher转换到teacherDto,按照以前的方式:把teacher中的List遍历,挨个转换成dto再手动添加到teacherDto中的list中
使用mapStruct:
@Mapper(componentModel = "spring")
public interface StudentConverter {
//定义student转studentDto
@Mapping(target = "gender", source = "sex")
StudentDto studentToDto(Student student);
//List<Student> 转 List<StudentDto>,此方法在编译生成后会调用StudentDto studentToDto(Student student);
//如果不定义StudentDto studentToDto(Student student); 此方法会报错
List<StudentDto> studentToDtoList(List<Student> students);
//Teacher 转 TeacherDto ,此方法调用 List<StudentDto> studentToDtoList(List<Student> students);
// 如果没有List<StudentDto> studentToDtoList(List<Student> students); 此方法报错
@Mapping(target = "studentDtos", source = "students")
TeacherDto teacherToDto(Teacher teacher);
}
测试:
public class ApplicationTests {
@Autowired
StudentConverter studentConverter;
@Test
public void contextLoads() {
Student student1 = new Student();
student1.setId(1);
student1.setName("sda");
student1.setAge(12);
student1.setSex("男");
Student student2 = new Student();
student2.setId(2);
student2.setName("asdfd");
student2.setAge(23);
student2.setSex("男");
Teacher teacher = new Teacher();
teacher.setStudents(Arrays.asList(student1, student2));
TeacherDto teacherDto = studentConverter.teacherToDto(teacher);
System.out.println("teacher = " + teacher);
System.out.println("teacherDto = " + teacherDto);
}
}
结果:
teacher = Teacher(students=[Student(id=1, name=sda, age=12, sex=男), Student(id=2, name=asdfd, age=23, sex=男)])
teacherDto = TeacherDto(studentDtos=[StudentDto(id=1, name=sda, age=12, gender=男), StudentDto(id=2, name=asdfd, age=23, gender=男)])
多对一
这里以二对一为例:
实体类:
@Data
public class School {
private Long id;
private String address;
private String name;
}
@Data
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
}
@Data
public class Teacher {
private List<Student> students;
}
对应Dto:
@Data
public class SchoolDto {
private Long id;
private String address;
private String name;
private List<TeacherDto> teacherDtos;
}
@Data
public class StudentDto {
private Integer id;
private String name;
private Integer age;
private String gender;
}
@Data
public class TeacherDto {
private List<StudentDto> studentDtos;
}
mapper类:
@Mapper(componentModel = "spring")
public interface StudentConverter {
//定义student转studentDto
@Mapping(target = "gender", source = "sex")
StudentDto studentToDto(Student student);
//List<Student> 转 List<StudentDto>,此方法在编译生成后会调用StudentDto studentToDto(Student student);
//如果不定义StudentDto studentToDto(Student student); 此方法会报错
List<StudentDto> studentToDtoList(List<Student> students);
//Teacher 转 TeacherDto ,此方法调用 List<StudentDto> studentToDtoList(List<Student> students);
// 如果没有List<StudentDto> studentToDtoList(List<Student> students); 此方法报错
@Mapping(target = "studentDtos", source = "students")
TeacherDto teacherToDto(Teacher teacher);
//多对一时,每个属性最好写上起对应关系,并且此方法能正确执行依赖 TeacherDto teacherToDto(Teacher teacher);
//如果teacherToDto方法不存在则会报错
@Mapping(target = "teacherDtos", source = "teachers")
@Mapping(target = "name", source = "school.name")
@Mapping(target = "id", source = "school.id")
@Mapping(target = "address", source = "school.address")
SchoolDto schoolToDto(School school, List<Teacher> teachers);
}
测试:
public class EurkaApplicationTests {
@Autowired
StudentConverter studentConverter;
@Test
public void convert() {
Student student1 = new Student();
student1.setId(1);
student1.setName("sda");
student1.setAge(12);
student1.setSex("男");
Student student2 = new Student();
student2.setId(2);
student2.setName("asdfd");
student2.setAge(23);
student2.setSex("男");
Teacher teacher1 = new Teacher();
teacher1.setStudents(Arrays.asList(student1, student2));
Teacher teacher2 = new Teacher();
teacher2.setStudents(Arrays.asList(student1, student2));
List<Teacher> teachers = Arrays.asList(teacher1, teacher2);
School school = new School();
school.setId(123L);
school.setAddress("育碧,苏维埃");
school.setName("育碧一中");
//调用mapper转换
SchoolDto schoolDto = studentConverter.schoolToDto(school, teachers);
System.out.println("school = " + school);
System.out.println("teachers = " + teachers);
System.out.println("schoolDto = " + schoolDto);
}
}
运行结果:
school = School(id=123, address=育碧,苏维埃, name=育碧一中)
teachers = [
Teacher(students=[Student(id=1, name=sda, age=12, sex=男), Student(id=2, name=asdfd, age=23, sex=男)]),
Teacher(students=[Student(id=1, name=sda, age=12, sex=男), Student(id=2, name=asdfd, age=23, sex=男)])
]
schoolDto = SchoolDto(
id=123,
address=育碧,
苏维埃,
name=育碧一中,
teacherDtos=[
TeacherDto(studentDtos=[StudentDto(id=1, name=sda, age=12, gender=男),StudentDto(id=2, name=asdfd, age=23, gender=男)]),
TeacherDto(studentDtos=[StudentDto(id=1, name=sda, age=12, gender=男), StudentDto(id=2, name=asdfd, age=23, gender=男)])
])