likes
comments
collection
share

「Java 开发工具」Java 实体映射工具类 MapStruct

作者站长头像
站长
· 阅读数 2

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

「Java 开发工具」Java 实体映射工具类 MapStruct

零、引入

(1)MapStruct 是用来干什么的?

将多个对象封装成一个对象进行输出到前端,例如用户user表需要直到 role角色的部分信息,

现在有这么个场景,从数据库查询出来了一个user对象(包含id,用户名,密码,手机号,邮箱,角色这些字段)和一个对应的角色对象role(包含id,角色名,角色描述这些字段),现在在controller需要用到user对象的id,用户名,和角色对象的角色名三个属性。

一种方式是直接把两个对象传递到controller层,但是这样会多出很多没用的属性。更通用的方式是需要用到的属性封装成一个类(DTO),通过传输这个类的实例来完成数据传输。

一、使用准备

(1)添加相关依赖
<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 插件
<!--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>

二、使用步骤

// 准备工作,准备两个演示实体类 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)定义转换接口
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) 最基本的对象映射测试
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测试依赖,如没有则请添加:(为了测试)

<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.6.2</version>
    <scope>test</scope>
</dependency>
(2) List 批量转换

第一步:书写转换方法和规则。

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);
}

第二步:编写测试类演示。

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

@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 就会自动实现该接口

@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.

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;有一定的局限性。

附录

  1. MapStruct 官网:MapStruct – Java bean mappings, the easy way!
  2. MapStruct 官方使用实例:GitHub - mapstruct/mapstruct-examples: Examples for using MapStruct
  3. 详细学习地址:blog.csdn.net/wangxueqing…
  4. 微信学习地址:mp.weixin.qq.com/s/HsxLBqo6n…
  5. 项目演示地址:GitSuperDrew / SpringBootDemo / springboot-mapstruct 模块。

🙏至此,感谢阅读🙏