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

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

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

截止目前, 星球 内专栏累计输出 72w+ 字,讲解图 3103+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2400+ 小伙伴加入学习 ,欢迎点击围观

在项目中,我们经常会遇到需要把一个对象转换为另一个对象的需求,比如数据库对象转换成 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 也提供了灵活而强大的配置选项。