广告
返回顶部
首页 > 资讯 > 后端开发 > Python >详解Java中的mapstruct插件使用
  • 845
分享到

详解Java中的mapstruct插件使用

2024-04-02 19:04:59 845人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

实体类的属性映射怎么可以少了它? 我们都知道,随着一个工程的越来越成熟,模块划分会越来越细,其中实体类一般存于 domain 之中,但 domain 工程最好不要被其他工程依赖,所以

实体类的属性映射怎么可以少了它?

我们都知道,随着一个工程的越来越成熟,模块划分会越来越细,其中实体类一般存于 domain 之中,但 domain 工程最好不要被其他工程依赖,所以其他工程想获取实体类数据时就需要在各自工程写 model,自定义 model 可以根据自身业务需要映射相应的实体属性。这样一来,这个映射工程貌似并不简单了。阿森差点就犯难了……

所以阿淼今天就要给大家安利一款叫 mapstruct插件,它就是专门用来处理 domin 实体类与 model 类的属性映射的,我们只需定义 mapper 接口,mapstruct 在编译的时候就会自动的帮我们实现这个映射接口,避免了麻烦复杂的映射实现。

那可能有的小伙伴就要问了?为啥不用 BeanUtilscopyProperties 方法呢?不也照样可以实现属性的映射么?

这个啊,阿淼我开始也是好奇,所以就和 BeanUtils 深入交流了一番,最后才发现,BeanUtils 就是一个大老粗,只能同属性映射,或者在属性相同的情况下,允许被映射的对象属性少;但当遇到被映射的属性数据类型被修改或者被映射的字段名被修改,则会导致映射失败。而 mapstruct 就是一个巧媳妇儿了,她心思细腻,把我们可能会遇到的情况都给考虑到了(要是阿淼我也能找一个这样的媳妇儿该多好,内心笑出了猪声)

如下是这个插件的开源项目地址和各种例子:

  • GitHub地址:https://github.com/mapstruct/mapstruct/
  • 使用例子:Https://github.com/mapstruct/mapstruct-examples

一、准备工作

接下来,阿淼将和大家一起去解开这个巧媳妇儿的真正面纱,所以我们还需要做一点准备工作。

1.1、了解@Mapper 注解

mybatis3.4.0 开始加入的 @Mapper 注解,目的就是为了不再写mapper映射文件。

我们只需要在 dao 层定义的接口上使用注解就可以实现sql语句的编写,例如:

@Select("select * from user where name = #{name}")
public User find(String name);

如上就是一个简单的使用,虽然简单,但也确实体现出了这个注解的优越性,至少少写了一个xml文件。

但阿淼我今天可不是想跟你探讨 @Mapper 注解,我主要是想去看我的巧媳妇儿 mapstruct ,所以我就只是想说下 @Mapper 注解的 componentModel 属性,componentModel 属性用于指定自动生成的接口实现类的组件类型,这个属性支持四个值:

 

  • default: 这是默认的情况,mapstruct 不使用任何组件类型, 可以通过Mappers.getMapper(Class)方式获取自动生成的实例对象。
  • cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
  • spring: 生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入
  • jsr330: 生成的实现类上会添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取

1.2、依赖包

首先需要把依赖包导入,主要由两个包组成:

  • org.mapstruct:mapstruct:包含了一些必要的注解,例如@Mapping。r若我们使用的jdk版本高于1.8,当我们在pom里面导入依赖时候,建议使用坐标是:org.mapstruct:mapstruct-jdk8,这可以帮助我们利用一些Java8的新特性。
  • org.mapstruct:mapstruct-processor:注解处理器,根据注解自动生成mapper的实现。
<dependency>
        <groupId>org.mapstruct</groupId>
        <!-- jdk8以下就使用mapstruct -->
        <artifactId>mapstruct-jdk8</artifactId>
        <version>1.2.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>1.2.0.Final</version>
    </dependency>

好了,准备工作做完了,接下来我们就看看巧媳妇儿巧在什么地方吧。

二、先简单玩一把

2.1、定义实体类以及被映射类

// 实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
    private Integer id;
    private String name;
    private String createTime;
    private LocalDateTime updateTime;
}
// 被映射类VO1:和实体类一模一样
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserVO1 {
    private Integer id;
    private String name;
    private String createTime;
    private LocalDateTime updateTime;
}
// 被映射类VO1:比实体类少一个字段
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserVO2 {
    private Integer id;
    private String name;
    private String createTime;
}

2.2、定义接口:

当实体类和被映射对象属性相同或者被映射对象属性值少几个时:

@Mapper(componentModel = "spring")
public interface UserCovertBasic {
    UserCovertBasic INSTANCE = Mappers.getMapper(UserCovertBasic.class);
    
    UserVO1 toConvertVO1(User source);
    User fromConvertEntity1(UserVO1 userVO1);
    
    UserVO2 toConvertVO2(User source);
}

从上面的代码可以看出:接口中声明了一个成员变量INSTANCE,母的是让客户端可以访问 Mapper 接口的实现。

2.3、使用

@RestController
public class TestController {
    @GetMapping("convert")
    public Object convertEntity() {
        User user = User.builder()
                .id(1)
                .name("张三")
                .createTime("2020-04-01 11:05:07")
                .updateTime(LocalDateTime.now())
                .build();
        List<Object> objectList = new ArrayList<>();
        objectList.add(user);
        // 使用mapstruct
        UserVO1 userVO1 = UserCovertBasic.INSTANCE.toConvertVO1(user);
        objectList.add("userVO1:" + UserCovertBasic.INSTANCE.toConvertVO1(user));
        objectList.add("userVO1转换回实体类user:" + UserCovertBasic.INSTANCE.fromConvertEntity1(userVO1));
        // 输出转换结果
        objectList.add("userVO2:" + " | " + UserCovertBasic.INSTANCE.toConvertVO2(user));
        // 使用BeanUtils
        UserVO2 userVO22 = new UserVO2();
        BeanUtils.copyProperties(user, userVO22);
        objectList.add("userVO22:" + " | " + userVO22);
        return objectList;
    }
}

2.4、查看编译结果

通过IDE的反编译功能查看编译后自动生成 UserCovertBasic 的实现类 UserCovertBasicImpl ,内容如下:

@Component
public class UserCovertBasicImpl implements UserCovertBasic {
    public UserCovertBasicImpl() {
    }
    public UserVO1 toConvertVO1(User source) {
        if (source == null) {
            return null;
        } else {
            UserVO1 userVO1 = new UserVO1();
            userVO1.setId(source.getId());
            userVO1.setName(source.getName());
            userVO1.setCreateTime(source.getCreateTime());
            userVO1.setUpdateTime(source.getUpdateTime());
            return userVO1;
        }
    }
    public User fromConvertEntity1(UserVO1 userVO1) {
        if (userVO1 == null) {
            return null;
        } else {
            User user = new User();
            user.setId(userVO1.getId());
            user.setName(userVO1.getName());
            user.setCreateTime(userVO1.getCreateTime());
            user.setUpdateTime(userVO1.getUpdateTime());
            return user;
        }
    }
    public UserVO2 toConvertVO2(User source) {
        if (source == null) {
            return null;
        } else {
            UserVO2 userVO2 = new UserVO2();
            userVO2.setId(source.getId());
            userVO2.setName(source.getName());
            userVO2.setCreateTime(source.getCreateTime());
            return userVO2;
        }
    }
}

2.5、浏览器查看结果

好了,一个流程就走完了,是不是感觉贼简单呢?

而且呀,阿淼温馨提醒:
如果是要转换一个集合的话,只需要把这里的实体类换成集合就行了,例如:

List<UserVO1> toConvertVOList(List<User> source);

三、不简单的情况

上面已经把整个流程都给过了一遍了,相信大家对 mapstruct 也有了一个基础的了解了,所以接下来的情况我们就不展示全部代码了,毕竟篇幅也有限,所以就直接上关键代码(因为不关键的和上面内容一样,哈哈)

3.1、类型不一致

实体类我们还是沿用 User;被映射对象 UserVO3 改为:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserVO3 {
    private String id;
    private String name;
    // 实体类该属性是String
    private LocalDateTime createTime;
    // 实体类该属性是LocalDateTime
    private String updateTime;
}

那么我们定义的接口就要稍稍修改一下了:

 @Mappings({
            @Mapping(target = "createTime", expression = "java(com.java.mmzsblog.util.DateTransfORM.strToDate(source.getCreateTime()))"),
    })
    UserVO3 toConvertVO3(User source);
    User fromConvertEntity3(UserVO3 userVO3);

上面 expression 指定的表达式内容如下:

public class DateTransform {
    public static LocalDateTime strToDate(String str){
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyy-MM-dd HH:mm:ss");
        return LocalDateTime.parse("2018-01-12 17:07:05",df);
    }
}

通过IDE的反编译功能查看编译后的实现类,结果是这样子的:

从图中我们可以看到,编译时使用了expression中定义的表达式对目标字段 createTime 进行了转换;然后你还会发现 updateTime 字段也被自动从 LocalDateTime 类型转换成了 String 类型。

阿淼小结:

当字段类型不一致时,以下的类型之间是 mapstruct 自动进行类型转换的:

  • 1、基本类型及其他们对应的包装类型。
  • 此时 mapstruct 会自动进行拆装箱。不需要人为的处理
  • 2、基本类型的包装类型和string类型之间

除此之外的类型转换我们可以通过定义表达式来进行指定转换。

3.2、字段名不一致

实体类我们还是沿用 User;被映射对象 UserVO4 改为:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserVO4 {
    // 实体类该属性名是id
    private String userId;
    // 实体类该属性名是name
    private String userName;
    private String createTime;
    private String updateTime;
}

那么我们定义的接口就要稍稍修改一下了:

@Mappings({
            @Mapping(source = "id", target = "userId"),
            @Mapping(source = "name", target = "userName")
    })
    UserVO4 toConvertVO(User source);
    
    User fromConvertEntity(UserVO4 userVO4);

通过IDE的反编译功能查看编译后的实现类,编译后的结果是这样子的:

很明显, mapstruct 通过读取我们配置的字段名对应关系,帮我们把它们赋值在了相对应的位置上,可以说是相当优秀了,但这也仅仅是优秀,而更秀的还请继续往下看:

阿淼小结:

当字段名不一致时,通过使用 @Mappings 注解指定对应关系,编译后即可实现对应字段的赋值。

3.3、属性是枚举类型

实体类我们还是改用 UserEnum:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserEnum {
    private Integer id;
    private String name;
    private UserTypeEnum userTypeEnum;
}

被映射对象 UserVO5 改为:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserVO5 {
    private Integer id;
    private String name;
    private String type;
}

枚举对象是:

@Getter
@AllArgsConstructor
public enum UserTypeEnum {
    Java("000", "Java开发工程师"),
    DB("001", "数据库管理员"),
    linux("002", "Linux运维员");
    private String value;
    private String title;
}

那么我们定义的接口还是照常定义,不会受到它是枚举就有所变化:

 @Mapping(source = "userTypeEnum", target = "type")
    UserVO5 toConvertVO5(UserEnum source);
    UserEnum fromConvertEntity5(UserVO5 userVO5);

通过IDE的反编译功能查看编译后的实现类,编译后的结果是这样子的:

很明显, mapstruct 通过枚举类型的内容,帮我们把枚举类型转换成字符串,并给type赋值,可谓是小心使得万年船啊。看来这巧媳妇儿不仅仅优秀还心细啊……

源代码

文章中的所有例子已上传github:https://github.com/mmzsblog/mapstructDemo

到此这篇关于详解Java中的mapstruct使用的文章就介绍到这了,更多相关Java mapstruct使用内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 详解Java中的mapstruct插件使用

本文链接: https://www.lsjlt.com/news/145071.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解Java中的mapstruct插件使用
    实体类的属性映射怎么可以少了它? 我们都知道,随着一个工程的越来越成熟,模块划分会越来越细,其中实体类一般存于 domain 之中,但 domain 工程最好不要被其他工程依赖,所以...
    99+
    2022-11-13
  • Java中的MapStruct用法详解
    目录1 MapStruct配置2 原理&性能2.1 实现原理3 使用方法3.1 转换器的检索3.1.1 使用Mappers工厂获取3.1.2 通过依赖注入的方式获取3.2 简...
    99+
    2022-11-13
  • 详解Java对象转换神器MapStruct库的使用
    目录前言MapStruct简介MapStruct入门1. 引入依赖2. 需要转换的对象3. 创建转换器4. 验证5. 自动生成的实现类MapStruct进阶场景1:属性名称不同、(基...
    99+
    2022-11-13
  • Java实体映射工具MapStruct使用方法详解
    目录1.序 2.简单用例 3.使用详解 1)关于接口注解@Mapper几种属性用法详解 2) 其他方法级别注解 总结1.序 通常在后端开发中经常不直接返回实体Entity类,经过处...
    99+
    2022-11-12
  • SpringBoot + MapStruct 属性映射工具的使用详解
    1. MapStruct 是什么? 截取下官方的原话 我给翻译了一下 说白了 当你的对象A有几十个属性 而另一个对象B 与A比较只有一些细微的差别 那么这时候只需要映射过去即可 而...
    99+
    2022-11-12
  • 详解MyBatisPlus中分页插件的使用
    目录MyBatis Plus分页插件使用自定义分页功能MyBatis Plus分页插件使用 MyBatis Plus中使用分页插件也很简单: 首先编写配置类: @Configurat...
    99+
    2023-02-09
    MyBatis Plus分页插件使用 MyBatis Plus分页插件 MyBatis Plus分页
  • IDEA插件FindBugs的使用详解
    前言 Findbugs很多人都并不陌生,Eclipse中有插件可以帮助查找代码中隐藏的bug,IDEA中也有这款插件。这个插件可以帮助我们查找隐藏的bug,比较重要的功能就是查找...
    99+
    2022-11-11
  • unity avprovideo插件的使用详解
    1、新建一个MediaPlayer组件 2、在canvas下新建一个AVProVideo组件 并将上一步新建的MediaPlayer组件赋值到avprovideo组件上的media...
    99+
    2022-11-12
  • jQuery插件ajaxFileUpload使用详解
    jQuery插件ajaxFileUpload是一个用于实现文件上传功能的插件。使用该插件可以通过AJAX方式将文件上传到服务器,并且...
    99+
    2023-08-17
    jQuery
  • Vue标尺插件使用详解
    本文为大家分享了Vue标尺插件使用的具体代码,供大家参考,具体内容如下 可根据高和宽度自适应,主要传值为宽度和距离零刻度的距离,代码和图片如下。 <template>...
    99+
    2022-11-13
  • 【Maven】打包插件使用详解
    本文参照官网:http://maven.apache.org/plugins 一.常用Maven打包插件: 1.spring-boot-maven-plugin:这个插件是springboo...
    99+
    2023-09-25
    maven java spring boot
  • Spring Boot Maven插件使用详解
    Spring Boot Maven插件提供了使用Spring Boot应用程序步骤如下:重新打包:创建一个可自动执行的jar或war文件。它可以替换常规工件,或者可以使用单独的分类器附加到构建生命周期。运行:运行您的Spring引导应用程序...
    99+
    2023-05-30
    spring boot maven
  • MyBatisPlus PaginationInterceptor分页插件的使用详解
    实现 配置插件 来到项目下的applicationContext.xml中配置sqlSessionFactoryBean的地方。 <!-- 配置SqlSessionFact...
    99+
    2022-11-11
  • 在vue项目中使用Swiper插件详解
    目录vue项目使用Swiper插件第一步:下载Swiper插件 第二步:在对应的组件页面中导入该插件第三步: 编写对应的html部分第四步:在data中配置对应的参数vue...
    99+
    2023-01-14
    vue Swiper vue使用Swiper插件 vue Swiper插件
  • Android Studio中ButterKnife插件的安装与使用详解
    1》Android Studio 安装ButterKnife插件同安装其他插件类似,如下:1》打开Plugins界面按照上图中1,2,3指示操作(注意:这里我的Android Studio中已经安装了该插件,所以显示的内容不太一样)。然后重...
    99+
    2023-05-30
  • LogicFlow插件使用前准备详解
    目录推荐几个好用的工具进入正题1. 安装插件扩展模块:2. 注册插件到全局或实例总结推荐几个好用的工具 var-conv 适用于VSCode IDE的代码变量名称快速转换...
    99+
    2023-01-29
    LogicFlow插件用前准备 LogicFlow插件
  • highlight.js代码高亮插件的使用详解
    在网页使用过程中,经常会用到代码的展示。而不同颜色的代码,可以让代码看起来更直观,也更美观。 找了几个不同的插件,觉得highlight的插件比较实用,而且用起来炒鸡简单。 比如这样...
    99+
    2022-11-12
  • springboot2 使用activiti6 idea插件的过程详解
    Activiti BPMN visualizer插件 import com.alibaba.fastjson.JSON; import com.schinta.util.D...
    99+
    2022-11-13
  • SQLMAP插件tamper编写与使用详解
    目录一、SQLMAP插件tamper简介二、SQLMAP插件tamper使用三、SQLMAP插件tamper编写今天继续给大家介绍渗透测试相关知识,本文主要内容是SQLMAP插件tamper编写与使用。 免责声明:本文所...
    99+
    2022-08-26
  • Spring Boot 中PageHelper 插件使用配置思路详解
    使用思路 1.引入myabtis和pagehelper依赖 2.yml中配置mybatis扫描和实体类 这2行代码 pageNum:当前第几页 pageSize:显示多少条数据 u...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作