mapstruct在SpringBoot中的使用


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=男)])
])

文章作者: Ubi-potato
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ubi-potato !
评论
  目录