文章目录
本文介绍
MapStruct 可以将某几种类型的对象映射为另外一种类型,如将多个 DO(业务实体对象) 对象转换为 DTO(数据传输对象)。
除了MapStruct,我们之前还使用过set/get,BeanUtils,当然还有其他的方式,他们的优劣我们下面讨论,我们先介绍MapStruct的基本使用。
基本使用
Student和StudentDTO转换
- 将student对象,转换成studentdto对象
- 将student集合转换成 studentdto集合
model模型
student
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="Student对象", description="")
public class Student extends Model<Student> {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "id")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "学号")
private String stuId;
@ApiModelProperty(value = "名字")
private String name;
@ApiModelProperty(value = "性别 0 男,1 女")
private Integer sex;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "个人简介")
private String info;
@ApiModelProperty(value = "头像,picture表id")
private Long picture;
@ApiModelProperty(value = "班级")
private String className;
@ApiModelProperty(value = "专业")
private String major;
@ApiModelProperty(value = "所在小组")
private Long gId;
}
studentdto对象
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "学生DTO对象", description = "返回某一个学生最基本的信息")
public class StudentDTO {
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "名字")
private String name;
@ApiModelProperty(value = "性别 0 男,1 女")
private Integer sex;
@ApiModelProperty(value = "班级")
private String className;
@ApiModelProperty(value = "学号")
private Long stuId;
@ApiModelProperty(value = "手机号")
private String phone;
}
使用流程
1. 引入依赖
<!--Java 实体映射工具 —— mapStruct依赖-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.2.0.Final</version>
</dependency>
2. 编写对象转换接口
通常在server层。
package com.marchsoft.group.manager.system.service.mapstruct;
/**
* @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
* 在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制。 注解详解看下方。
*/
@Mapper
public interface StudentMapStruct {
/**
* 获取该类自动生成的实现类的实例
* 接口中的属性都是 public static final 的 方法都是public abstract的
*/
StudentMapStruct INSTANCES = Mappers.getMapper(StudentMapStruct.class);
//转换对象
StudentDTO toStudentDTO(Student student);
//转换集合
List<StudentDTO> toListStudentDTO(List<Student> student);
}
3. 初次使用
//设置每页的容量为5,这里是一个分页查询。
Integer pageCount=2;
Page<Student> page = new Page<>(pageCurrent,pageCount);
Page<Student> studentPage = studentMapper.selectPage(page, null);
//获取数据库查询的对象
List<Student> students = studentPage.getRecords();
//转换成DTO集合
List<StudentDTO> studentsDTO = StudentMapStruct.INSTANCES.toListStudentDTO(students);
System.out.println(students);
System.out.println(studentsDTO);
4.结果
StudentDTO(id=1, name=张三, sex=0, className=动4561, stuId=2004, phone=19907865257)
[StudentDTO(id=3, name=花花, sex=1, className=动546, stuId=2045614106, phone=1990245212), StudentDTO(id=4, name=花花, sex=1, className=动科181, stuId=2078788107, phone=199885454512)]
源码分析
创建StudentMapStruct接口后,运行,系统会默认帮我们生成它的实现类。底层是使用最基本的set/get
进阶使用
MapStruct提供的一些处理器选项配置
MapStruct为我们提供了 @Mapper 注解,并提供了一些属性配置。
如:我们常用的两种componentModel和unmappedTargetPolicy
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface StudentMapStruct {
}
1.componentModel = “XX” 四个属性值
- default: 默认的情况,mapstruct不使用任何组件类型, 可以通过Mappers.getMapper(Class)方式获取自动生成的实例对象。
- cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
- spring(经常使用): 生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入
- jsr330: 生成的实现类上会添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取。
2. unmappedTargetPolicy=ReportingPolicy.XX 三个属性值
在未使用source值填充映射方法的target的属性的情况下要应用的默认报告策略
- IGNORE 将被忽略
- WARN 构建时引起警告
- ERROR 映射代码生成失败
其他的选项配置
使用Spring依赖
我们使用了componentModel=“spring”,我们就不需要创建StudentMapStrct的实例了,直接通过 @Autowired即可使用
接口中:
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface StudentMapStruct {
StudentDTO toStudentDTO(Student student);
List<StudentDTO> toListStudentDTO(List<Student> student);
}
注入:
@Autowired
StudentMapStruct studentMapStruct;
直接使用:
StudentDTO studentDTO = studentMapStruct.toStudentDTO(student);
List<StudentDTO> studentsDTO =studentMapStruct.toListStudentDTO(students);
属性名称不一致
- 属性不一致时,我们可以使用@Mapping
@Mapping(source = "name", target = "stuName")
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface StudentMapStruct {
@Mapping(source = "name", target = "stuName")
StudentDTO toStudentDTO(Student student);
@Mapping(source = "name", target = "stuName")
List<StudentDTO> toListStudentDTO(List<Student> student);
}
属性类型不一致
当类型不一致时,mapstruct会帮我们做自动转换,但是自动转换类型有限
能够自动转换的类型
- 基本类型及其包装类
- 基本类型的包装类型和String类型之间
- String类型和枚举类型之间
- 自定义常量
实体模型
@Data
public class UserTest {
private String gender;
private Integer age;
}
@Data
public class UserTestDTO {
private boolean sex;
private String age;
}
类型转换类
要加入到bean容器中,因为之后当我们需要使用时,他会自动的被调用
@Component
public class BooleanStrFormat {
public String toStr(Boolean gender) {
if (gender) {
return "男";
} else {
return "女";
}
}
public Boolean toBoolean(String str) {
if (str.equals("Y")) {
return true;
} else {
return false;
}
}
}
映射接口
使用uses={BooleanStrFormat.class})引入转换类,自动会调用
@Component
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE,uses={BooleanStrFormat.class})
public interface UserStruct {
@Mappings({
@Mapping(source = "gender",target = "sex"),
})
UserTestDTO toUserTestDTO(UserTest userTest);
}
测试类
@Autowired
UserStruct userStruct;
@Test
public void mapStructTest(){
UserTest userTest = new UserTest();
userTest.setGender("Y");
userTest.setAge(18);
UserTestDTO userTestDTO = userStruct.toUserTestDTO(userTest);
System.out.println(userTestDTO);
}
结果:
UserTestDTO(sex=true, age=18)
多个source映射一个target
有时,我们可能会遇到将两个不同的对象的值,赋给同一个对象,此时我们就需要使用@Mapping这个注解。
@Mapper(componentModel = "spring")
public interface GoodInfoMapper {
@Mappings({
@Mapping(source = "type.name",target = "typeName"),
@Mapping(source = "good.id",target = "goodId"),
@Mapping(source = "good.title",target = "goodName"),
@Mapping(source = "good.price",target = "goodPrice")
})
GoodInfoDTO fromGoodInfoDTO(GoodInfo good, GoodType type);
}
日期转换/固定值
固定值:constant
@Mapping(source = "name", constant = "hollis")
日期转换:dateForma
@Mapping(source = "person.begin",target="birth" dateFormat="yyyy-MM-dd HH:mm:ss")
封装使用
我们可以封装一个struct的类,来满足所有对象对映射的基本使用
详细步骤:
1.创建公共的处理类BaseMapper
public interface BaseMapper<D, E> {
/**
* DTO转Entity
* @param dto /
* @return /
*/
E toEntity(D dto);
/**
* Entity转DTO
* @param entity /
* @return /
*/
D toDto(E entity);
/**
* DTO集合转Entity集合
* @param dtoList /
* @return /
*/
List <E> toEntity(List<D> dtoList);
/**
* Entity集合转DTO集合
* @param entityList /
* @return /
*/
List <D> toDto(List<E> entityList);
}
2.创建每一个实体对象的struct,继承BaseMapper
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface StudentMapStruct extends BaseMapper<StudentDTO,Student> {
}
3.使用
StudentDTO studentDTO = studentMapStruct.toDto(student);
List<StudentDTO> studentsDTO =studentMapStruct.toDto(students);
4.结果
StudentDTO(id=1, name=张三, sex=0, className=动4561, stuId=2004, phone=19907865257)
[StudentDTO(id=3, name=花花, sex=1, className=动546, stuId=2045614106, phone=1990245212), StudentDTO(id=4, name=花花, sex=1, className=动科181, stuId=2078788107, phone=199885454512)]
性能对比
注解介绍
常见大坑:
如果我们项目运行之后,在数据库中添加了某个字段,此时我们重新运行项目时,如果改字段涉及到mapstruct的转换,那么在自动生成的MapStructImpl中是没有这个字段的,不论有没有值,我们获取的都是null,此时需要我们清除环境,再重新运行代码。
参考blog:
写此文章时,我正处于初步学习阶段,感谢这些文章对我的帮助学习。