MapStruct基本配置使用

5,192 阅读6分钟

本文介绍

MapStruct 可以将某几种类型的对象映射为另外一种类型,如将多个 DO(业务实体对象) 对象转换为 DTO(数据传输对象)。 除了MapStruct,我们之前还使用过set/get,BeanUtils,当然还有其他的方式,他们的优劣我们下面讨论,我们先介绍MapStruct的基本使用。

基本使用

Student和StudentDTO转换

  1. 将student对象,转换成studentdto对象
  2. 将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</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${mapstruct.version}</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会帮我们做自动转换,但是自动转换类型有限

能够自动转换的类型

  1. 基本类型及其包装类
  2. 基本类型的包装类型和String类型之间
  3. String类型和枚举类型之间
  4. 自定义常量

实体模型

@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)]

性能对比

在这里插入图片描述

注解介绍

在这里插入图片描述

参考blog:

写此文章时,我正处于初步学习阶段,感谢这些文章对我的帮助学习。

  1. MapStruct超级简单的学习笔记
  2. 丢弃掉那些BeanUtils工具类吧,MapStruct真香!!!
  3. 为什么阿里巴巴禁止使用Apache Beanutils进行属性的copy?
  4. MapStruct入门指南