MapStruct初窥门径

一、介绍

MapStruct相比于BeanUtils性能更高,能够实现DO,DTO,VO之间的转换,达到解耦合的目的

二、使用前提

  1. 添加依赖
		
            org.mapstruct
            mapstruct
            1.5.5.Final
        
        
            org.mapstruct
            mapstruct-processor
            1.5.5.Final
            provided
        
  1. 新建一个接口StudentConvert,实现不同POJO之间的转换
  • 接口中需要使用MapStruct的一个注解@Mapper,利用MapStruct的工厂创建一个studentConvert类,可以通过该类调用类中的方法
  1. 新建DO,DTO,VO类
  • DO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentDO {

    private Integer id;

    private String name;

    private String password;

    private String phoneNumber;

    private Integer gender;
    
    private Double price;
    
    private Date birthDay;
    
    private SubjectDO subjectDO;

}
@Data
public class SubjectDO {

    private Integer id;

    private String name;
}
  • DTO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentDTO {

    private Integer id;

    private String studentName;

    private String phoneNumber;

    private String gender;
    
    private String price;
    
    private String birthDay;
    
    private SubjectDTO subjectDTO;

}
@Data
public class SubjectDTO {

    private Integer subjectId;

    private String subjectName;
}
  • VO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {

    private Integer id;

    private String studentName;

    private Boolean hasPhoneNumber;

    private String gender;
}

三、使用示例

1.DO 转 DTO

  • 转换逻辑
@Mapper
public interface StudentConvert {

    StudentConvert INSTANCE = Mappers.getMapper(StudentConvert.class);

    /**
     * DO convert to DTO
     * @param studentDO StudentDO
     * @return com.haomiao.algorithm.pojo.StudentDTO
     */
    StudentDTO doToDto(StudentDO studentDO);
}
  • 进行测试
	@Test
    void doConvertDto(){
        StudentDO studentDO = buildStudentDO();
        StudentDTO studentDTO = StudentConvert.INSTANCE.doToDto(studentDO);
        System.out.println(studentDTO);
    }
    
	StudentDO buildStudentDO(){
        StudentDO studentDO = new StudentDO();
        studentDO.setId(1);
        studentDO.setName("zhangsan");
        studentDO.setPassword("Qw123");
        studentDO.setPhoneNumber("15271861495");
        studentDO.setGender(1);
        studentDO.setPrice(22.1234d);
        studentDO.setBirthDay(new Date());
        studentDO.setSubjectDO(buildSubjectDO());
        return studentDO;
    }
    
    SubjectDO buildSubjectDO(){
        SubjectDO subjectDO = new SubjectDO();
        subjectDO.setId(12);
        subjectDO.setName("数学");
        return subjectDO;
    }
    
  • 输出结果为:
StudentDTO(id=1, name=null, phoneNumber=15271861495, gender=1, birthDay=23-10-4 下午5:25, price=22.1234, subjectDTO=null)

因此得出结论:

  1. 同类型同名的属性会自动映射
  2. 就算不是同类型,也会自动进行类型转换
  • 8种基本类型及其对应的包装类型之间会自动转换
  • 8种基本类型(及其对应的包转类型)和String会自动转换
  • 日期类型和String之间会自动转换

四、@Mappings注解

@Mappings由多个@Mapping注解组成

作用:自定义映射

1.小数格式化

注意使用numberFormat时,小数转换为小数是不行的

  • 将Double类型保留两位小数,映射为String类型
	@Mappings(
            @Mapping(source = "price",target = "price",numberFormat = "#.00")
    )
    StudentDTO doToDto(StudentDO studentDO);
  • 输出结果为:
StudentDTO(id=1, name=null, phoneNumber=15271861495, gender=1, birthDay=23-10-4 下午5:51, price=22.12, subjectDTO=null)

2.时间格式化
  • 使用dateFormat
	@Mappings(
            value = {
                    @Mapping(source = "price",target = "price",numberFormat = "#.00"),
                    @Mapping(source = "birthDay",target = "birthDay",dateFormat = "yyyy-MM-dd HH:mm:ss")
            }
    )
    StudentDTO doToDto(StudentDO studentDO);
  • 输出结果为:
StudentDTO(id=1, name=null, phoneNumber=15271861495, gender=1, birthDay=2023-10-04 17:57:22, price=22.12, subjectDTO=null)
3.忽略某个属性映射
  • 使用ignore
@Mappings(
            value = {
                    @Mapping(source = "price",target = "price",numberFormat = "#.00"),
                    @Mapping(source = "birthDay",target = "birthDay",dateFormat = "yyyy-MM-dd HH:mm:ss"),
                    @Mapping(target = "phoneNumber",ignore = true)
            }
    )
    StudentDTO doToDto(StudentDO studentDO);
  • 输出结果为:
StudentDTO(id=1, name=null, phoneNumber=null, gender=1, birthDay=2023-10-04 18:02:17, price=22.12, subjectDTO=null)

4.名称不同映射
@Mappings(
            value = {
                    @Mapping(source = "price",target = "price",numberFormat = "#.00"),
                    @Mapping(source = "birthDay",target = "birthDay",dateFormat = "yyyy-MM-dd HH:mm:ss"),
                    @Mapping(target = "phoneNumber",ignore = true),
                    @Mapping(source = "name",target = "studentName")
            }
    )
    StudentDTO doToDto(StudentDO studentDO);
  • 输出结果为:
StudentDTO(id=1, studentName=zhangsan, phoneNumber=null, gender=1, birthDay=2023-10-04 18:16:04, price=22.12, subjectDTO=null)
5.复杂类型转换
  • 在转换方法中,对复杂类型再添加一个转换规则,如下:
@Mapper
public interface StudentConvert {

    StudentConvert INSTANCE = Mappers.getMapper(StudentConvert.class);

    /**
     * DO convert to DTO
     * @param studentDO StudentDO
     * @return com.haomiao.algorithm.pojo.StudentDTO
     */
    @Mappings(
            value = {
                    @Mapping(source = "price",target = "price",numberFormat = "#.00"),
                    @Mapping(source = "birthDay",target = "birthDay",dateFormat = "yyyy-MM-dd HH:mm:ss"),
                    @Mapping(target = "phoneNumber",ignore = true),
                    @Mapping(source = "name",target = "studentName"),
                    @Mapping(source = "subjectDO",target = "subjectDTO")
            }
    )
    StudentDTO doToDto(StudentDO studentDO);

    /**
     * DO convert to DTO
     * @param subjectDO SubjectDO
     * @return com.haomiao.algorithm.pojo.SubjectDTO
     */
    @Mapping(source = "id",target = "subjectId")
    @Mapping(source = "name",target = "subjectName")
    SubjectDTO subjectDo2SubjectDto(SubjectDO subjectDO);
}
  • 输出结果为:
StudentDTO(id=1, studentName=zhangsan, phoneNumber=null, gender=1, birthDay=2023-10-04 18:27:28, price=22.12, subjectDTO=SubjectDTO(subjectId=12, subjectName=数学))

  • 可以观察生成的代码
    MapStruct初窥门径_第1张图片
6.MapStruct规则无法映射,自定义映射规则
  • 使用@AfterMapping和@MappingTarget注解

注意:这种使用场景需要自己实现方法逻辑,因此需要转换的类为抽象类,而非接口,之前的案例使用接口实现,这里可以参考别人的抽象类实现:

MapStruct初窥门径_第2张图片

7.批量转换

MapStruct初窥门径_第3张图片

8.BeanMapping使用(适用于只需要映射少量字段的情况)

MapStruct初窥门径_第4张图片

9.InheritConfigration

MapStruct初窥门径_第5张图片

10.反向继承
  • 注意:只会继承@Mapping注解,不会继承BeanMapping注解
    MapStruct初窥门径_第6张图片
11.结合Spring

在这里插入图片描述

你可能感兴趣的:(java,java)