Mapstruct 简单对象之间映射(超详细)

更新时间 2023-08-17 15:15:48

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 / 赠书活动

目前, 星球 内第2个项目《仿小红书(微服务架构)》正在更新中。第1个项目:全栈前后端分离博客项目已经完结,演示地址:http://116.62.199.48/。采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 255 小节,累计 39w+ 字,讲解图:1716 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 1300+ 小伙伴加入,欢迎点击围观

在项目中,我们经常会遇到需要把一个对象转换为另一个对象的需求,比如数据库对象转换成 DTO (数据传输)对象。本小节中,小哈将演示如何使用 MapStruct 实现简单对象之间的映射。

1. 创建源对象和目标对象

首先,我们定义两个简单的 Java 对象:User 作为源对象和 UserDTO 作为目标对象。

源对象

public class User {
    /**
     * ID
     */
    private Long id;
    /**
     * 用户名
     */
    private String username;
    /**
     * 邮箱
     */
    private String email;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

目标对象

public class UserDTO {
    /**
     * ID
     */
    private Long id;
    /**
     * 用户名
     */
    private String username;
    /**
     * 邮箱
     */
    private String email;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "UserDTO{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

注意:这里 UserUserDTO 的字段名完全匹配的。

2. 定义映射接口

接下来,我们需要定义一个接口,MapStruct 会在编译时基于这个接口生成实际的映射类。

@Mapper
public interface UserConvert {
	// 静态实例
    UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);

    UserDTO toUserDTO(User user);

    User toUser(UserDTO userDTO);
}

这里,我们使用了 @Mapper 注解来声明这是一个 MapStruct 映射器,并定义了两个映射方法。

3. 实现映射

由于 MapStruct 在编译时会为我们自动生成映射的实现,所以我们实际上不需要手动写这些实现代码。我们只需要在需要的地方通过 UserMapper.INSTANCE 调用映射方法即可。

我们可以执行 mvn clean compile 命令,看看编译后的源文件,是否已经自动生成了映射相关的代码:

Mapstruct 自动生成了映射代码Mapstruct 自动生成了映射代码

可以看到,Mapstruct 自动生成了 UserConvert 的实现类,相关映射方法也实现好了,终于不用手动 set 方法了。

4. 如何使用?

新建一个单元测试方法,使用方法如下:

@Test
public void testConvert() {
    User user = new User();
    user.setId(1L);
    user.setEmail("quanxiaoha@qq.com");
    user.setUsername("犬小哈");

    UserDTO userDTO = UserConvert.INSTANCE.toUserDTO(user);
    // 打印对象,看看是否映射成功
    System.out.println(userDTO);

}

执行该单元测试,控制台输出如下:

可以看到字段都映射成功了。

5. 自定义字段映射

如果源对象和目标对象的字段名不完全匹配,MapStruct 提供了 @Mapping 注解来自定义字段之间的映射关系。

@Mapper
public interface UserConvert {
    UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
    
    @Mapping(source = "username", target = "name")
    @Mapping(source = "email", target = "emailAddress")
    @Mapping(source = "id", target = "identifier")
    UserDTO toUserDTO(User user);
    
    @Mapping(source = "name", target = "username")
    @Mapping(source = "emailAddress", target = "email")
    @Mapping(source = "identifier", target = "id")
    User toUser(UserDTO userDTO);
}

这里,source 是源对象的字段名,target 是目标对象的字段名。

6. 小结

MapStruct 为我们提供了一个简洁、高效的方式来实现对象之间的映射。它遵循“约定优于配置”的原则,对于大多数常见的映射场景,我们不需要写任何实现代码。当需要自定义映射时,MapStruct 也提供了灵活而强大的配置选项。