前提引入:重点官网 mapstruct 官网:mapstruct.org/

零、引入
(1)MapStruct 是用来干什么的?
将多个对象封装成一个对象进行输出到前端,例如用户user表需要直到 role角色的部分信息,
现在有这么个场景,从数据库查询出来了一个user对象(包含id,用户名,密码,手机号,邮箱,角色这些字段)和一个对应的角色对象role(包含id,角色名,角色描述这些字段),现在在controller需要用到user对象的id,用户名,和角色对象的角色名三个属性。
一种方式是直接把两个对象传递到controller层,但是这样会多出很多没用的属性。更通用的方式是需要用到的属性封装成一个类(DTO),通过传输这个类的实例来完成数据传输。
一、使用准备
(1)添加相关依赖
xml
<properties>
<mapstruct.version>1.3.0.Final</mapstruct.version>
</properties>
<dependencies>
<!-- mapstruct相关依赖包-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<!-- 实体简化类 Lombok (按照最新的 Lombok 就好) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
(2)指定 maven-compiler-plugin 插件
xml
<!--maven编译默认5.0,使用maven-compiler-plugin插件指定jdk版本,1.6以上-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version> <!-- or newer version -->
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
</annotationProcessorPaths>
</configuration>
</plugin>
二、使用步骤
java
// 准备工作,准备两个演示实体类 Student.java 和 StudentDTO.java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private Integer stuId;
private String stuName;
private Integer stuAge;
private GenderEnum stuSex;
}
public enum GenderEnum {
Male("1", "男"), Female("0", "女");
private String code;
private String name;
public String getCode() {return this.code;}
public String getName() {return this.name;}
GenderEnum(String code, String name) {this.code = code; this.name = name; }
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class StudentDTO {
private Integer id;
private String name;
private Integer age;
private String sex;
}
(1)定义转换接口
java
import com.study.module.mapstruct.dto.StudentDTO;
import com.study.module.mapstruct.entity.Student;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface StudentConverter {
StudentConverter INSTANCE = Mappers.getMapper(StudentConverter.class);
@Mappings({
@Mapping(source = "stuId", target = "id"),
@Mapping(source = "stuName", target = "name"),
@Mapping(source = "stuAge", target = "age"),
// @Mapping(source = "stuBirthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(source = "stuSex.name", target = "sex")
})
StudentDTO demain2DTO(Student student);
}
(2)maven
编译
mvn compiler
会得到一个 StudentConverter
的实现类。尽量保证编译无警告/错误.
三、测试演示
(1) 最基本的对象映射测试
java
import com.study.module.mapstruct.converter.StudentConverter;
import com.study.module.mapstruct.dto.StudentDTO;
import com.study.module.mapstruct.entity.Student;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class StudentConverterTest {
@Test
void demain2DTO() {
Student student = Student.builder()
.stuId(1).stuName("Drew").stuAge(12).stuSex(GenderEnum.Male)
.build();
StudentDTO studentDTO = StudentConverter.INSTANCE.demain2DTO(student);
// [{StudentDTO(id=1,name=Drew,age=12,sex=男)}]
System.out.println(studentDTO);
}
}
注意:查看 pom.xml 文件中是否有 junit测试依赖,如没有则请添加:(为了测试)
java
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
(2) List 批量转换
第一步:书写转换方法和规则。
java
import com.study.module.mapstruct.dto.StudentDTO;
import com.study.module.mapstruct.entity.Student;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface StudentConverter {
StudentConverter INSTANCE = Mappers.getMapper(StudentConverter.class);
// 1. 一对一不同属性名转换规则
@Mappings({
@Mapping(source = "stuId", target = "id"),
@Mapping(source = "stuName", target = "name"),
@Mapping(source = "stuAge", target = "age"),
// 如果target.sex没有赋值,则将定义的默认值给到target.sex=1
@Mapping(source = "stuSex.name", target = "sex", defaultValue = "男")
})
StudentDTO demain2DTO(Student student);
// 2. List映射:此时不需要任何其他的配置了,因为[1.]已经处理属性映射关系了
List<StudentDTO> demain2DTOs(List<Student> studentList);
}
第二步:编写测试类演示。
java
public static void main(String[] args) {
Student student = Student.builder().stuId(1).stuName("小明").stuAge(6).build();//注意此处不对stuSex赋值,测试转换使用默认值是否有效
List<Student> list = new ArrayList<>();
list.add(student);
// list转换
List<StudentDTO> result = StudentConverter.INSTANCE.demain2DTOs(list);
// 输出内容: [{StudentDTO(id=1,name=小明,age=6,sex=男)}] 注意,此处的"sex=男"使用了defaultValue默认值。
System.out.println(result);
}
(3) 多对象转换到一个对象 演示
1. 新建演示的 DO 和 DTO
例如:Sku 和 Item 两个DO,一个 SkuDTO
java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Item {
private Long id;
private String title;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Sku {
private Long id;
private String code;
private Integer price;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SkuDTO {
private Long skuId;
private String skuCode;
private Integer skuPrice;
private Long itemId;
private String itemName;
}
2. 创建 ItemConverter
(映射)接口
mapstruct
就会自动实现该接口
java
@Mapper
public interface ItemConverter {
ItemConverter INSTANCE = Mappers.getMapper(ItemConverter.class);
@Mappings({
@Mapping(source = "sku.id", target = "skuId"),
@Mapping(source = "sku.code", target = "skuCode"),
@Mapping(source = "sku.price", target = "skuPrice"),
@Mapping(source = "item.id", target = "itemId"),
@Mapping(source = "item.title", target = "itemName", defaultValue="默认itemName值")
})
SkuDTO domain2dto(Item item, Sku sku);
}
3. 创建测试类
将 item 和 sku 两个 DO 对象,映射成一个 DTO对象 SkuDTO.
java
public class ItemConverterTest {
@Test
public void test() {
Item item = new Item(1L, "iPhone X");
Sku sku = new Sku(2L, "phone12345", 1000000);
SkuDTO skuDTO = ItemConverter.INSTANCE.domain2dto(item, sku);
assertNotNull(skuDTO);
assertEquals(skuDTO.getSkuId(),sku.getId());
assertEquals(skuDTO.getSkuCode(),sku.getCode());
assertEquals(skuDTO.getSkuPrice(),sku.getPrice());
assertEquals(skuDTO.getItemId(),item.getId());
assertEquals(skuDTO.getItemName(),item.getTitle());
}
}
总结
对象之间的转换有多种方式,最常见的有如下两种:
- ① org.springframework.utils.BeanUtils 工具的 copyProperties(Source, Target);
- ② 第三方工具:MapStruct 工具;
- ③ * apache 的 Beanutils;有一定的局限性。
附录
- MapStruct 官网:MapStruct -- Java bean mappings, the easy way!
- MapStruct 官方使用实例:GitHub - mapstruct/mapstruct-examples: Examples for using MapStruct
- 详细学习地址:blog.csdn.net/wangxueqing...
- 微信学习地址:mp.weixin.qq.com/s/HsxLBqo6n...
- 项目演示地址:GitSuperDrew / SpringBootDemo / springboot-mapstruct 模块。

🙏至此,感谢阅读🙏