Java中MapStruct对象映射的实现

 更新时间:2024年12月06日 09:46:17   作者:沙坪坝、黄师傅  
MapStruct是一种Java实体类映射框架,本文就来介绍一下Java中MapStruct对象映射的实现,具有一定的参考价值,感兴趣的可以了解一下

简介

MapStruct是一种实体类映射框架,能够通过Java注解将一个实体类的属性安全地赋值给另一个实体类。有了mapstruct,只需要定义一个映射器接口,声明需要映射的方法,在编译过程中,mapstruct会自动生成该接口的实现类,实现将源对象映射到目标对象的效果。

MapStruct是基于JSR 269实现的,JSR 269是JDK引进的一种规范。有了它,能够实现在编译期处理注解,并且读取、修改和添加抽象语法树中的内容。JSR 269使用Annotation Processor在编译期间处理注解,Annotation Processor相当于编译器的一种插件,因此又称为插入式注解处理。官网通道 | Github

优点

  • 安全性高‌:由于映射是在编译期间实现的,如果编译器能够通过,运行期就不会报错。

  • 高性能:编译时生成bean映射的实现类,通过使⽤普通⽅法(getter/setter)调⽤⽽不是反射来快速执⾏。

缺点

  • 使用复杂度:对于更复杂的映射,需要开发人员编写自定义映射接口和函数。

同类对比

映射工具

实现机制

性能对比

备注

Dozer

反射机制

使用递归将数据从一个对象复制到另一个对象

Orika

反射机制

同Dozer,不过Orika 使用字节码生成

ModelMapper

反射机制

简单易用,它根据约定确定对象之间的映射方式

JMapper

编译生成

基于Javassist 的Java映射框架

MapStruct

编译生成

在编译时生成bean映射,以确保高性能、彻底的错误检查

快速入门

Maven依赖

<!-- 定义版本 -->
  <properties>
      <org.mapstruct.version>1.6.0</org.mapstruct.version>
      <org.projectlombok.mapstruct.version>0.2.0</org.projectlombok.mapstruct.version>
  </properties>

<!-- 依赖包 -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>${org.mapstruct.version}</version>
</dependency>

  <!--MapStruct会用到对象中的get、set方法,但get、set方法又需要lombok来生成。因此需要控制这两者工作顺序-->
 <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok-mapstruct-binding</artifactId>
      <version>${org.projectlombok.mapstruct.version}</version>
 </dependency>

编译插件

<!-- 方式一-->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>${org.mapstruct.version}</version>
    <scope>provided</scope>
</dependency>
  
<!-- 方式二-->
  <build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>17</source>
                <target>17</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

Model定义

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO implements Serializable {
    private Integer id;
    private String userName;
    private String password;
    private Integer age;
    private String address;
    private String email;
    private List<UserRole> roles;
}

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserVO implements Serializable {
    private Integer id;
    private String name;
    private String pwd;
    private Integer age;
    private String email;
    private List<UserRole> roles;
}

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserRole implements Serializable {
    private Integer roleId;
    private String roleName;
    private String remark;
}

Mapper定义

//@Mapper(componentModel = "spring")
@Mapper
public interface MapStructMapper {

    MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class);

    /**
     * 单个对象转换
     * @param userDTO
     * @return
     */
    UserVO userDtoToVO(UserDTO userDTO);

    /**
     * 集合对象转换
     * @param userDTO
     * @return
     */
    List<UserVO> userDtoToVOList(List<UserDTO> userDTO);

}

重要:SpringBoot项目可以使用@Mapper(componentModel = "spring")的方式将bean交给spring容器进行管理,因此可以不用写MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class);

转换调用

    public void mapStructConvertTest(){
        // 初始化用户
        UserDTO userDTO = this.instanceUser();
        UserVO userVO = MapStructMapper.INSTANCE.userDtoToVO(userDTO);
        log.info("userVO:{}", JSON.toJSONString(userVO));
    }

场景示例

常规映射

@Mapper
public interface MapStructMapper {

    MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class);

    UserVO userDtoToVO(UserDTO userDTO);

}

集合映射

@Mapper
public interface MapStructMapper {

    MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class);

    List<UserVO> userDtoToVOList(List<UserDTO> userDTO);

}

重要:集合转换时,必须先有单个对象的转换函数。并且Mapper中只能有一个同类的原对象与目标对象的转换,否则集合转换不知道取哪一个单对象转换函数。

单字段映射

@Mapping(target = "pwd", source = "password")
UserVO userDtoToVO(UserDTO userDTO);

多字段映射

@Mappings({
  @Mapping(target = "pwd", source = "password"),
  @Mapping(target = "name", source = "userName"),
})
UserVO userDtoToVOMoreField(UserDTO userDTO);

忽略字段

@Mapping(target = "email", ignore = true)
UserVO userDtoToVOIgnoreField(UserDTO userDTO);

常量值映射

@Mapping(target = "constant", constant = "OK")
UserVO newUserWithConstant(UserDTO userDTO);

默认值映射

@Mapping(source = "email", target = "email", defaultValue = "默认值")
UserVO userDtoToVONullDefaultValue(UserDTO userDTO);

表达式映射

@Mapping(target = "fullName", expression = "java(userDTO.getUserName() + ' ' + userDTO.getAddress())")
UserVO userDtoToVOExpression(UserDTO userDTO);
@Mapping(target = "email", 
expression = "java(!userDTO.getEmail().isEmpty()? \"不为空\" : \"为空\")")
UserVO userDtoToVOWithCondition(UserDTO userDTO);

执行函数

@Mapping(target = "email", source = "email", qualifiedByName = "toUpperCase")
UserVO emailToUpperCase(UserDTO userDTO);

@Named("toUpperCase")
default String toUpperCase(String value) {
    // 转换大写
    return value == null ? null : value.toUpperCase();
}

深拷贝

@Mapper(componentModel = "spring",mappingControl = DeepClone.class)
public interface MapStructMapper {
  
}

说明:mappingControl = DeepClone.class 是指定深拷贝模式,不指定则默认浅拷贝,浅拷贝时集合类是底层是调用Array 的copy 方法。如果是深拷贝模式,MapStruct框架会生成集合遍历代码,集合中元素如果是引用类型会生成引用类型转换代码,层层转换,深度拷贝。集合类拷贝的限制比较多,不支持多层嵌套集合类深拷贝,而且要求源字段和目标字段集合类型严格一致。

浅拷贝 :只复制对象的引用,而不会复制对象本身的内容。如果更改了原始对象的一个地址,DTO中的地址也会跟着改变,因为它们指向的是同一个对象。

深拷贝:会递归地复制对象的所有内容,包括嵌套的对象。即使你更改了原始对象中的数据,DTO中的数据也不会受到影响。

逆向映射

    /**
     * 单个对象映射
     * @param userDTO
     * @return
     */

    UserVO userDtoToVO(UserDTO userDTO);

    /**
     * 逆向映射
     * @param userVO  源 VO 对象
     * @return 目标 DTO 对象
     */
    @InheritInverseConfiguration
    UserDTO userVOToDto(UserVO userVO);

说明:正向函数有业务逻辑处理或属性类型不匹配的不能逆向映射

映射后执行动作

 @AfterMapping
default void afterMapping(UserDTO userDTO, @MappingTarget UserVO userVO) {
    // Add custom post-mapping logic here
    userVO.setId(10000);
    System.out.println("afterMapping:id设置为:"+userVO.getId());
}

延伸内容

MapStruct  Plus 是 MapStruct 的增强工具,在 MapStruct 的基础上,实现了自动生成 Mapper 接口的功能,并强化了部分功能,使 Java 类型转换更加便捷、优雅。官网通道 | GitHub

Maven依赖

<properties>
    <mapstruct-plus.version>1.4.5</mapstruct-plus.version>
</properties>
  
<dependency>
        <groupId>io.github.linpeilie</groupId>
        <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
        <version>${mapstruct-plus.version}</version>
    </dependency>

新增配置类

@ComponentModelConfig(componentModel = "default")
public class MapperConfiguration {
}

对象映射

@AutoMapper(target = UserDto.class)
@Data
public class User {
    // ...
}

转换Map

@AutoMapMapper
@Data
public class MapModelB {

    private Date date;

}

一个类转换为多个类

@Data
@AutoMappers({
    @AutoMapper(target = UserDto.class),
    @AutoMapper(target = UserVO.class)
})
public class User {
    // fields
}

循环嵌套

@Data
@AutoMapper(target = TreeNodeDto.class, cycleAvoiding = true)
public class TreeNode {
    private TreeNode parent;
    private List<TreeNode> children;
}

@Data
@AutoMapper(target = TreeNode.class, cycleAvoiding = true)
public class TreeNodeDto {
    private TreeNodeDto parent;
    private List<TreeNodeDto> children;
}

到此这篇关于Java中MapStruct对象映射的实现的文章就介绍到这了,更多相关MapStruct对象映射内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java中Calendar与Date类型互相转换的方法

    java中Calendar与Date类型互相转换的方法

    这篇文章主要介绍了java中Calendar与Date类型互相转换的方法,Calendar与Date类型是我们日常开发中常用的两种数据类型,它们用于不同的场景,两者具有不同的方法,接下来通过实例给大家详解,需要的朋友可以参考下
    2022-09-09
  • 关于Java并发编程中线程间协作的两种方式

    关于Java并发编程中线程间协作的两种方式

    这篇文章主要介绍了关于Java并发编程中线程间协作的两种方式,当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源的占用权,这是消费者模式,需要的朋友可以参考下
    2023-07-07
  • yaml文件中${}语法取值方式

    yaml文件中${}语法取值方式

    在Spring Boot中,配置文件中的${test.aa}等占位符的值可以通过系统属性或依赖的其他模块来获取,这意味着,可以通过JVM参数或者系统属性来指定这些值,例如,通过在启动命令中添加-Dtest.aa=your_value或在代码中通过
    2024-10-10
  • SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分库分表

    SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分库分表

    本文主要介绍了SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分库分表,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • 关于HashMap 并发时会引起死循环的问题解析

    关于HashMap 并发时会引起死循环的问题解析

    JDK1.8之前采用头插,即在链表结构上每次都把数据放在链表头部。JDK1.8采用尾插方法,很多朋友在学习Java并发容器和框架时,看到为什么要使用ConcurrentHashMap时不知道究其原因,今天小编通过本文给大家介绍下HashMap 并发死循环问题,一起看看吧
    2021-05-05
  • Spring中@order注解用法实战教程

    Spring中@order注解用法实战教程

    @Order注解主要用来控制配置类的加载顺序,数字越小,越先加载,下面这篇文章主要给大家介绍了关于Spring中@order注解用法的相关资料,需要的朋友可以参考下
    2022-11-11
  • mybatisplus下划线驼峰转换的问题解决

    mybatisplus下划线驼峰转换的问题解决

    在mybatis-plus中,下划线-驼峰自动转换可能导致带下划线的字段查询结果为null,本文就来介绍一下mybatisplus下划线驼峰转换的问题解决,感兴趣的可以了解一下
    2024-10-10
  • Java数据结构之位图的简单实现和使用

    Java数据结构之位图的简单实现和使用

    位图, 是一种非常常见的结构, 它使用每个二进制位来存放一个值的状态, 就类似于 Java 当中 HashSet 存储元素的功能。本文主要来介绍一下位图的简单实现和使用,需要的可以参考一下
    2023-05-05
  • 如何使用会话Cookie和Java实现JWT身份验证

    如何使用会话Cookie和Java实现JWT身份验证

    这篇文章主要介绍了如何使用会话Cookie和Java实现JWT身份验证,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • 往DAO类中注入@PersistenceContext和@Resource的区别详解

    往DAO类中注入@PersistenceContext和@Resource的区别详解

    这篇文章主要介绍了往DAO类中注入@PersistenceContext和@Resource的区别详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02

最新评论