一、介绍
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);
}
- 进行测试
@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)
因此得出结论:
- 同类型同名的属性会自动映射
- 就算不是同类型,也会自动进行类型转换
- 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注解