首页 > 其他分享 >MapStruct的介绍及入门使用

MapStruct的介绍及入门使用

时间:2024-06-01 14:23:44浏览次数:12  
标签:StudentDto 入门 MapStruct StudentMapper 介绍 class private student public

一、痛点

  代码中存在很多Java Bean之间的转换,编写映射转化代码是一个繁琐重复还易出错的工作。使用BeanUtils工具时,对于字段名不一致和嵌套类型不一致时,需要手动编写。并且基于反射,对性能有一定开销。Spring提供的BeanUtils针对apache的BeanUtils做了很多优化,整体性能提升了不少,不过还是使用反射实现,针对复杂场景支持能力不足。

二、MapStruct 机制

MapStruct是编译期动态生成getter/setter,在运行期直接调用框架编译好的class类实现实体映射。因此安全性高,编译通过之后,运行期间就不会报错。其次速度快,运行期间直接调用实现类,不会在运行期间使用发射进行转换。

三、环境搭建

Maven依赖导入:mapstruct依赖会导入MapStruct的核心注解。由于MapStruct在编译时工作,因此需要在<build>标签中添加插件maven-compiler-plugin,并在其配置中添加annotationProcessorPaths,该插件会在构建时生成对应的代码。

<properties>
    <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
  	<lombok.version>1.18.12</lombok.version>
</properties>

<dependencies>
    <dependency>
        <groupid>org.mapstruct</groupid>
        <artifactid>mapstruct</artifactid>
        <version>${org.mapstruct.version}</version>
    </dependency>
    <dependency>
        <groupid>org.projectlombok</groupid>
        <artifactid>lombok</artifactid>
        <version>${lombok.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupid>org.apache.maven.plugins</groupid>
            <artifactid>maven-compiler-plugin</artifactid>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationprocessorpaths>
                    <path>
                        <groupid>org.mapstruct</groupid>
                        <artifactid>mapstruct-processor</artifactid>
                        <version>${org.mapstruct.version}</version>
                    </path>
                  	<!--下面这个 项目中不使用 Lombok的话 不用加-->
                    <path>
                        <groupid>org.projectlombok</groupid>
                        <artifactid>lombok</artifactid>
                        <version>${lombok.version}</version>
                    </path>
                </annotationprocessorpaths>
            </configuration>
        </plugin>
    </plugins>
</build>

四、使用

单一对象转化

创建映射:如下两个类进行对象之间的转换

public class Student {
    private int id;
    private String name;
    // 两个类中存在不同的属性名,需要在Mapper接口中设置source和target
    private String book;  
    // getters and setters or builder
}

public class StudentDto {
    private int id;
    private String name;
    // 两个类中存在不同的属性名,需要在Mapper接口中设置source和target
    private String letter; 
    // getters and setters or builder
}

两者之间进行映射,需要创建一个StudentMapper接口并使用@Mapper注解,MapStruct就知道这是两个类之间的映射器。

@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    // 两个类中存在不同的属性名,需要在Mapper接口中设置source和target
    @Mapping(source = "student.book", target = "letter")
    StudentDto toDto(Student student);
}

当我们需要将Student属性映射到StudentDto

StudentDto studentDto = StudentMapper.INSTANCE.toDto(student);

当我们构建/编译应用程序时,MapStruct注解处理器插件会识别出StudentMapper接口并生成StudentMapperImpl实体类:如果类型中包含Builder, MapStruct会尝试使用它来构建实例,如果没有MapStruct将通过new关键字进行实例化。

public class StudentMapperImpl implements StudentMapper {
    @Override
    public StudentDto toDto(Student student) {
        if ( student == null ) {
            return null;
        }
        StudentDtoBuilder studentDto = StudentDto.builder();

        studentDto.id(student.getId());
        studentDto.name(student.getName());
        // ....
        return studentDto.build();
    }
}

多个对象转换为一个对象

public class Student {
    private int id;
    private String name;
    // getters and setters or builder
}

public class StudentDto {
    private int id;
    private int classId;
    private String name;
    // getters and setters or builder
}

public class ClassInfo {
    private int id;
    private int classId;
    private String className;
    // getters and setters or builder
}

StudentMapper接口更新如下:如果两个属性中包含相同的字段时,需要通过sourcetarget指定具体使用哪个类的属性。

@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "student.id", target = "id")    
    StudentDto toDto(Student student, ClassInfo classInfo);
}

子对象映射

多数情况下,POJO中不会只包含基本数据类型,其中往往会包含其它类。比如说,一个Student类中包含ClassInfo类:

public class Student {
    private int id;
    private String name;
    private ClassInfo classInfo;
    // getters and setters or builder
}

public class StudentDto {
    private int id;
    private String name;
    private ClassInfoDto classInfoDto;
    // getters and setters or builder
}

public class ClassInfo {
    private int classId;
    private String className;
    // getters and setters or builder
}

public class ClassInfoDto {
    private int classId;
    private String className;
    // getters and setters or builder
}

在修改StudentMapper之前,我们先创建一个ClassInfoMapper转换器:

@Mapper
public interface ClassInfoMapper {
    ClassInfoMapper INSTANCE = Mappers.getMapper(ClassInfoMapper.class);
    ClassInfoDto dto(ClassInfo classInfo);
}

创建完ClassInfoMapper之后,我们再修改StudentMapper:添加uses标识,这样StudentMapper就能够使用ClassInfoMapper映射器

@Mapper(uses = {ClassInfoMapper.class})
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source="student.classInfo", target="classInfoDto")
    StudentDto toDto(Student student, ClassInfo classInfo);
}

我们看先编译后的代码:新增了一个新的映射方法classInfoDtoToclassInfo。这个方法如果没有显示定义的情况下生成,因为我们将ClassInfoMapper对象添加到了StudenMapper中。

public class StudentMapperImpl implements StudentMapper {
    private final ClassInfoMapper classInfoMapper = Mappers.getMapper( ClassInfoMapper.class );

    @Override
    public StudentDto toDto(Student student) {
        if ( student == null ) {
            return null;
        }
        StudentDtoBuilder studentDto = StudentDto.builder();

        studentDto.id(student.getId());
        studentDto.name(student.getName());
        studentDto.classInfo = (classInfoDtoToclassInfo(student.calssInfo))
        // ....
        return studentDto.build();
    }

    protected ClassInfoDto classInfoDtoToclassInfo(ClassInfo classInfo) {
        if ( classInfo == null ) {
            return null;
        }
        ClassInfoDto classInfoDto = classInfoMapper.toDto(classInfo);
        return classInfoDto;
    }
}

数据类型映射
自动类型转换适用于一下几种情况:
【1】基本类型及其包装类之间的转换:int和Integer,float与Float,long与Long,boolean与Boolean等。
【2】任意基本类型与任意包装类之间。如int和long,byte和Integer等。
【3】所有基本类型及包装类与String之间。如boolean和String,Integer和String等。
【4】枚举和String之间。
【5】Java大数类型java.math.BigInteger, java.math.BigDecimal和Java基本类型(包括其包装类)与String之间。

日期转换:指定格式

public class Student {
    private int id;
    private String name;
    private LocalDate birth;
    // getters and setters or builder
}

public class StudentDto {
    private int id;
    private String name;
    pprivate String birth;
    // getters and setters or builder
}

创建映射器

@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    // 也可以指定数字的格式
    // @Mapping(source = "price", target = "price", numberFormat = "$#.00")
    @Mapping(source = "birth", target = "birth", dataFormat = "dd/MM/yyyy")    
    StudentDto toDto(Student student, ClassInfo classInfo);
}

List映射

定义一个新的映射方法

@Mapper
public interface StudentMapper {
    List<StudentDto> map(List<Student> student);
}

自动生成的代码如下:

public class StudentMapperImpl implements StudentMapper {

    @Override
    public List<StudentDto> map(List<Student> student) {
        if ( student == null ) {
            return null;
        }

        List<StudentDto> list = new ArrayList<StudentDto>( student.size() );
        for ( Student student1 : student ) {
            list.add( studentToStudentDto( student1 ) );
        }

        return list;
    }

    protected StudentDto studentToStudentDto(Student student) {
        if ( student == null ) {
            return null;
        }

        StudentDto studentDto = new StudentDto();

        studentDto.setId( student.getId() );
        studentDto.setName( student.getName() );

        return studentDto;
    }
}

SetMap型数据的处理方式与List相似:

@Mapper
public interface StudentMapper {

    Set<StudentDto> setConvert(Set<Student> student);

    Map<String, StudentDto> mapConvert(Map<String, Student> student);
}

添加默认值
@Mapping注解有两个很实用的标志就是常量constant和默认值defaultValue。无论source如何取值,都将始终使用常量值,如果source取值为null,则会使用默认值。修改一下StudentMapper,添加一个constant和一个defaultValue:

@Mapper(componentModel = "spring")
public interface StudentMapper {
    @Mapping(target = "id", constant = "-1")
    @Mapping(source = "student.name", target = "name", defaultValue = "zzx")
    StudentDto toDto(Student student);
}

如果name不可用,我们会替换为zzx字符串,此外,我们将id硬编码为-1

@Component
public class StudentMapperImpl implements StudentMapper {

    @Override
    public StudentDto toDto(Student student) {
        if (student == null) {
            return null;
        }

        StudentDto studentDto = new StudentDto();

        if (student.getName() != null) {
            studentDto.setName(student.getName());
        }
        else {
            studentDto.setName("zzx");
        }
        studentDto.setId(-1);

        return studentDto;
    }
}

添加表达式

MapStruct甚至允许在@Mapping注解中输入Java表达式。你可以设置defaultExpression

@Mapper(componentModel = "spring", imports = {LocalDateTime.class, UUID.class})
public interface StudentMapper {

    @Mapping(target = "id", expression = "java(UUID.randomUUID().toString())")
    @Mapping(source = "student.availability", target = "availability", defaultExpression = "java(LocalDateTime.now())")
    StudentDto toDtoWithExpression(Student student);
}

五、依赖注入

如果你使用的是Spring,只需要修改映射器配置,在Mapper注解中添加componentModel = "spring",告诉MapStruct在生成映射器实现类时,支持通过Spring的依赖注入来创建,就不需要在接口中添加INSTANCE字段了。这次生成的StudentMapperImpl会带有@Component注解,就可以在其它类中通过@Autowire注解来使用它。

@Mapper(componentModel = "spring")
public interface StudentMapper {}

如果你不使用SpringMapStruct也支持Java CDI

@Mapper(componentModel = "cdi")
public interface StudentMapper {}

转载自https://blog.csdn.net/zhengzhaoyang122/article/details/132657876

标签:StudentDto,入门,MapStruct,StudentMapper,介绍,class,private,student,public
From: https://www.cnblogs.com/liftsail/p/18225940

相关文章

  • web前端三大主流框架详细介绍
    1.AngularAngular是一个由Google开发的用于构建Web应用的开源JavaScript框架。Angular使用TypeScript语言编写,它是一种由Microsoft开发的JavaScript超集,可以提供更丰富的功能和更严格的类型检查。Angular是MVC(Model-View-Controller)框架,它提供了一种结构化的方法来开发Web应用......
  • VALL-EX下载介绍:只需3秒录音,即可克隆你的声音
    VALL-EX是一个强大和创新的多语言文本转语音模型,支持对中文、英文和日语的语音进行合成和克隆,使用者只需上传一段3-10秒的录音,就可以生成高质量的目标音频,同时保留了说话人的声音、情感和声学环境VALL-EX的应用范围非常广泛,可以用于跨语言文本到语音、语音合成和语音到语音翻译......
  • 下面提供一些C语言的入门示例代码
    下面提供一些C语言的入门示例代码,并附有注释,以帮助理解每个部分的功能。1.HelloWorld程序#include<stdio.h> //引入标准输入输出库intmain(){ //主函数的开始   printf("Hello,World!\n"); //打印"Hello,World!"到控制台   return0; //返回......
  • VALL-EX下载介绍:只需3秒录音,即可克隆你的声音
    VALL-EX是一个强大和创新的多语言文本转语音模型,支持对中文、英文和日语的语音进行合成和克隆,使用者只需上传一段3-10秒的录音,就可以生成高质量的目标音频,同时保留了说话人的声音、情感和声学环境VALL-EX的应用范围非常广泛,可以用于跨语言文本到语音、语音合成和语音到语音翻......
  • 旅游门票预订系统功能介绍
    功能特性为你介绍旅游门票预订系统的功能特性景点项目支持发布多个景点项目、景点门票等。在线支付支持整合微信支付功能退款价格支持3个时段退款扣费标准扫码核销每个景点订单都有独立的核销码Uniapp开发基于Uniapp方式使用Vue2进行开发,小程序加载更快,体验更好旅......
  • 今天介绍的DC-DC升压恒压方案是TY3001
        今天介绍的DC-DC升压恒压方案是TY3001特性:  输入电压:0.9V~6.5V  输出电压:1.8V~5.0V(步进0.1V)(固定电压)  输出精度:±2.5%  最高效率:94%  最高工作频率:300KHz 电路图: 封装......
  • gRPC入门学习之旅(九)
    gRPC入门学习之旅目录 gRPC入门学习之旅(一)gRPC入门学习之旅(二)gRPC入门学习之旅(三)gRPC入门学习之旅(四)gRPC入门学习之旅(七) 3.10、客户端编译生成GRPC类1.在“解决方案资源管理器”中,使用鼠标左键选中项目名称“Demo.Grpc.Common”,然后单击鼠标右键,在弹出......
  • 运用JavaScript代码,使用Three.js框架在网页中实现3D效果,零基础入门Three.js,包含具体实
    不经意间看到了某个大佬做的网站~实在是太帅啦!查了查实现该效果的技术——原来是Three.js如果你也感兴趣的话,那就让我们来从零开始学习Three.js动态3D效果吧✨一、了解Three.js是基于原生WebGL封装运行的三维引擎,在所有WebGL引擎中,Three.js是国内文资料最多、使......
  • 基础入门
    域名:www.baidu.com分类:二级域名和多级域名DNS(域名系统服务协议):主要用于域名和IP地址的相互转换,根据域名查出IP地址CDN(内容分发网络):尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输得更快、更稳定常见的脚本语言(网站程序的源代码所用的语言,语言的严......
  • SQL入门全攻略(一)
    一、引言在当今的数据驱动世界中,SQL(结构化查询语言)无疑是数据处理和分析的基石。无论你是数据科学家、数据库管理员还是业务分析师,掌握SQL都是必不可少的技能。本文将带你从SQL的基础知识开始,逐步深入,让你能够轻松上手并应用SQL。二、SQL基础1.SQL是什么?SQL是一种用于管理(......