Article Directory
This article introduces
MapStruct can map certain types of objects to another type, such as converting multiple DO (business entity objects) objects into DTO (data transfer objects).
In addition to MapStruct, we have also used set/get, BeanUtils before, and of course there are other methods. We will discuss their pros and cons below. Let's first introduce the basic use of MapStruct.
Basic use
Student and StudentDTO conversion
- Convert the student object into a studentdto object
- Convert the student collection to the studentdto collection
model 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 object
@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;
}
manual
1. Introduce dependencies
<!--Java 实体映射工具 —— mapStruct依赖-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${
mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${
mapstruct.version}</version>
</dependency>
2. Writing the object conversion interface
is usually at the server layer.
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. First use
//设置每页的容量为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. Results
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)]
Source code analysis
After creating the StudentMapStruct interface and running it, the system will generate its implementation class for us by default. The bottom layer is to use the most basic set/get
Advanced use
Some processor option configurations provided by MapStruct
MapStruct provides us with @Mapper annotations and some property configurations.
Such as: two commonly used componentMode l and unmappedTargetPolicy
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface StudentMapStruct {
}
1.componentModel = "XX" four attribute values
- default: By default, mapstruct does not use any component types, and the automatically generated instance objects can be obtained through Mappers.getMapper(Class).
- cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
- Spring (used frequently): An @Component annotation will be automatically added to the generated implementation class, which can be injected through Spring's @Autowired method
- jsr330: @javax.inject.Named and @Singleton annotations will be added to the generated implementation class, which can be obtained through the @Inject annotation.
2. unmappedTargetPolicy=ReportingPolicy.XX three attribute values
In not using the default policy report the case of a source attribute value to fill the target mapping method to be applied
-IGNORE will be ignored
-WARN causes a warning when building
-ERROR mapping code generation failed
Other options configuration
Use Spring dependencies
We use componentModel="spring", we don’t need to create an instance of StudentMapStrct, it can be used directly through @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);
Inconsistent attribute names
- When the attributes are inconsistent, we can use @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);
}
Inconsistent attribute types
When the types are inconsistent, mapstruct will help us to do automatic conversion, but the automatic conversion type is limited
Types that can be automatically converted
- Basic types and their packaging
- Between the basic type of packaging type and the String type
- Between String type and enumeration type
- Custom constant
Entity model
@Data
public class UserTest {
private String gender;
private Integer age;
}
@Data
public class UserTestDTO {
private boolean sex;
private String age;
}
The type conversion class
should be added to the bean container, because later when we need to use it, it will be called automatically
@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;
}
}
}
Mapping interface
Use uses={BooleanStrFormat.class}) to introduce the conversion class, which will be called automatically
@Component
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE,uses={
BooleanStrFormat.class})
public interface UserStruct {
@Mappings({
@Mapping(source = "gender",target = "sex"),
})
UserTestDTO toUserTestDTO(UserTest userTest);
}
Test class
@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);
}
result:
UserTestDTO(sex=true, age=18)
Multiple sources map a target
Sometimes, we may encounter assigning the values of two different objects to the same object. At this time, we need to use the @Mapping annotation.
@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);
}
Date conversion/fixed value
固定值:constant
@Mapping(source = "name", constant = "hollis")
日期转换:dateForma
@Mapping(source = "person.begin",target="birth" dateFormat="yyyy-MM-dd HH:mm:ss")
Package use
We can encapsulate a struct class to satisfy the basic use of mapping for all objects
detailed steps:
1. Create a public processing class 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. Create a struct for each entity object, inherit BaseMapper
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface StudentMapStruct extends BaseMapper<StudentDTO,Student> {
}
3. Use
StudentDTO studentDTO = studentMapStruct.toDto(student);
List<StudentDTO> studentsDTO =studentMapStruct.toDto(students);
4. Results
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)]
Performance comparison
Annotation introduction
Reference blog:
At the time of writing this article, I was in the preliminary learning stage, and I am grateful to these articles for helping me learn.