前言

具体为啥要用就不说了,请看下文。

我们知道,随着项目规模的扩大,清晰的模块划分和项目层次划分是势在必行的,如此我们不仅可以实现高效开发,还能实现代码之间的解耦合。

在一个成熟的项目工程中,常见的,一般会划分多个 DOVODTO 对象,DO一般指的是我们的业务实体对象,可以理解成我们对数据库关系的映射;VO一般指的是我们业务需要返回给前端的一些字段(显然,我们返回给前端的字段不可能是和数据库中一一对应,针对不同的业务我们或许会做一些增删或者增强的操作);而DTO在我的理解,如分布式应用或者第三方服务之间的对象传递,也可以理解成在后端传递的对象,对我们的前台业务没有影响。

如上,我们的项目划分了如此多的层,那么针对各个对象字段间的转换,就会相对麻烦。在之前我们或许会使用 BeanUtils这样的工具,但是它相对于 Mapsruct 而言的话,所支持的功能就太少了,而且并不安全,它只能同属性映射,遇到被映射的数属性数据类型被修改或者字段名被修改的时候,也会导致映射失败,总之风险性极高。而 MapStruct 就解决了这一系列的问题。

官网地址
GitHub地址

优点

  1. 和手动生成映射代码来对比,更加节省时间并且提高准确率。
  2. 简单泛型智能转换
  3. 效率高
  4. 性能高,仅仅使用简单的 Java 方法来调用来替代传统的反射映射
  5. 编译时类型安全,只能映射相同名称或者带有映射标记的属性
  6. 编译时产生异常,如果映射不完整或者不正确,那么会在编译时抛出异常

Maven依赖

<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-jdk8 -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.3.1.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.3.1.Final</version>
</dependency>

我们可以导入Maven仓库的稳定版本,如果我们使用的 JDK 版本高于1.8,那么我们可以使用如上所示的包,这样可以帮助我们利用一些 Java8 中所提供的新特性。
processor 库则是注解处理器,根据注解自动生成 Mapper 的实现。

在SpringBoot中使用

关于报错

在使用 IDEA2020.3 版本作为集成开发环境的时候,编译过程或许会出现空指针异常错误。

解决方法:
Setting —>Build,Execution,Deployment —>Compiler —>User-local build
加上参数: -Djps.track.ap.dependencies=false

定义接口

package com.imsle.mapstructexample.common.mapper;

import com.imsle.mapstructexample.common.dto.UserDTO1;
import com.imsle.mapstructexample.common.dto.UserDTO2;
import com.imsle.mapstructexample.common.dto.UserDTO3;
import com.imsle.mapstructexample.common.po.UserPO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

/**
 * @作者: Seale
 * @时间: 2021/07/22 5:00 下午
 * @说明:
 * @类名: UserPOToUserDTO
 */
@Mapper
public interface UserPOToUserDTO {
    // ClassLoader 加载方式
    UserPOToUserDTO INSTANCE = Mappers.getMapper(UserPOToUserDTO.class);

    // 字段类型相同
    UserDTO1 userPoToUserDto1(UserPO userPO);

    UserPO userDto1ToUserPo(UserDTO1 userDTO1);

    // 字段类型数量不一致
    UserDTO2 userPoToUserDto2(UserPO userPO);

    // 字段名称不一致
    @Mappings({
            @Mapping(source = "name", target = "frontName"),
            @Mapping(source = "id", target = "frontId"),
            @Mapping(source = "createTime", target = "frontCreateTime")
    })
    UserDTO3 userPoToUserDto3(UserPO userPO);

    @Mappings({
            @Mapping(source = "frontName", target = "name"),
            @Mapping(source = "frontId", target = "id"),
            @Mapping(source = "frontCreateTime", target = "createTime")
    })
    UserPO userDto3ToUserPo(UserDTO3 userDTO3);
}
package com.imsle.mapstructexample.common.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * @作者: Seale
 * @时间: 2021/07/22 4:51 下午
 * @说明: 被映射的UserDTO对象,和实体类的每一个属性名都不一样
 * @类名: UserDTO3
 */
@Data
@Accessors(chain = true)
public class UserDTO3 {
    @JsonProperty("front_id")
    private Integer frontId;
    @JsonProperty("front_name")
    private String frontName;
    @JsonProperty("front_create_time")
    private String frontCreateTime;
}

package com.imsle.mapstructexample.common.po;

import lombok.Data;
import lombok.experimental.Accessors;

/**
 * @作者: Seale
 * @时间: 2021/07/22 4:49 下午
 * @说明: UserDO 对象
 * @类名: UserDO
 */
@Data
@Accessors(chain = true)
public class UserPO {
    private Integer id;
    private String name;
    private String createTime;
}

进行测试

在这里我就没有使用 Junit 进行测试了,直接使用一个 Controller 进行测试。

package com.imsle.mapstructexample.controller;

import com.imsle.mapstructexample.common.dto.UserDTO3;
import com.imsle.mapstructexample.common.mapper.UserPOToUserDTO;
import com.imsle.mapstructexample.common.po.UserPO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @作者: Seale
 * @时间: 2021/07/22 5:25 下午
 * @说明:
 * @类名: TestController
 */
@RestController
public class TestController {

    @GetMapping("/test")
    public Object poToDto(){
        UserPO userPO = new UserPO();
        userPO.setId(1)
                .setCreateTime("测试时间")
                .setName("测试姓名");
        // 添加结果列表
        List<Object> objects = new ArrayList<>();
        objects.add(userPO);

        // 使用mapStruct转换
        UserDTO3 userDTO3 = UserPOToUserDTO.INSTANCE.userPoToUserDto3(userPO);
        objects.add(userDTO3);

        return objects;

    }
}

结果

QQ20210722-1739552x.png

后续

对于 MapStruct 本篇博文只是做简单入门介绍和使用教程,它的强大远不止于此,更多可以关注官网中的文档。


本作品采用知识共享署名 4.0 国际许可协议进行许可。

如果可以的话,请给我钱请给我点赞赏,小小心意即可!

Last modification:February 26, 2023
If you think my article is useful to you, please feel free to appreciate