java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MapStruct对象映射

Java中MapStruct对象映射的实现

作者:沙坪坝、黄师傅

MapStruct是一种Java实体类映射框架,本文就来介绍一下Java中MapStruct对象映射的实现,具有一定的参考价值,感兴趣的可以了解一下

简介

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

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

优点

缺点

同类对比

映射工具

实现机制

性能对比

备注

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对象映射内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文