Mapstruct应用

  |   0 评论   |   0 浏览

Mapstruct应用

1.说明

MapStruct是一个代码生成器,它极大地简化了基于约定优于配置方法的Java bean类型之间映射的实现。
生成的映射代码使用普通的方法调用,因此快速、类型安全且易于理解。

官方地址:https://mapstruct.org/

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);

    }

}

测试结果

image-20231007102223681

在使用的过程中只需要短短的一行代码就可以实现类型转换,那么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);

    }

}

测试结果

image-20231007104915014

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);

    }

}

测试结果

image-20231007105438139

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);

    }

}

可以看到默认值对于集合的转换并不会生效

image-20231007105946136

编译后的接口实现类

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();
        }
    }
}

标题:Mapstruct应用
作者:llp
地址:https://llinp.cn/articles/2023/10/07/1696649033072.html