一、介绍
MapStruct相比于BeanUtils性能更高,能够实现DO,DTO,VO之间的转换,达到解耦合的目的
二、使用前提
- 添加依赖
<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.5.5.Final</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.5.5.Final</version><scope>provided</scope></dependency>
- 新建一个接口StudentConvert,实现不同POJO之间的转换
- 接口中需要使用MapStruct的一个注解@Mapper,利用MapStruct的工厂创建一个studentConvert类,可以通过该类调用类中的方法
- 新建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);
}
- 进行测试
@Testvoid 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)
因此得出结论:
- 同类型同名的属性会自动映射
- 就算不是同类型,也会自动进行类型转换
- 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=数学))
- 可以观察生成的代码
6.MapStruct规则无法映射,自定义映射规则
- 使用@AfterMapping和@MappingTarget注解
注意:这种使用场景需要自己实现方法逻辑,因此需要转换的类为抽象类,而非接口,之前的案例使用接口实现,这里可以参考别人的抽象类实现:
7.批量转换
8.BeanMapping使用(适用于只需要映射少量字段的情况)
9.InheritConfigration
10.反向继承
- 注意:只会继承@Mapping注解,不会继承BeanMapping注解
11.结合Spring
- 注意:如果要使用Spring容器注入,需要删除下面代码
StudentConvert INSTANCE = Mappers.getMapper(StudentConvert.class);