mapstruct实现各个实体间的类型转换(DTO转BO、BO转Entity)的实践

news2024/11/20 22:38:01

一、引入

在没有遇见mapstruct的时候,实现各个实体之间的转换,都是手动转换实现的,属性少一带你还好,当属性一多,代码就会变得很冗余,没必要的非逻辑的代码就会加多。。。。

比如:

public class UserDTO {
    private String username;
    private String email;
    private boolean isActive;

    // Getters and setters
    // Constructor
    // Other methods as needed
}

// BO: UserBO.java
public class UserBO {
    private String username;
    private String email;
    private boolean isActive;

    // Getters and setters
    // Constructor
    // Business methods as needed
}

手动实现类型转换:

public class UserConverter {

    public static UserBO convertToBO(UserDTO userDTO) {
        UserBO userBO = new UserBO();
        userBO.setUsername(userDTO.getUsername());
        userBO.setEmail(userDTO.getEmail());
        userBO.setActive(userDTO.isActive());
        return userBO;
    }

    public static UserDTO convertToDTO(UserBO userBO) {
        UserDTO userDTO = new UserDTO();
        userDTO.setUsername(userBO.getUsername());
        userDTO.setEmail(userBO.getEmail());
        userDTO.setActive(userBO.isActive());
        return userDTO;
    }
}

这种方式,就很费时费力,一个突然的契机,我在学习DDD(领域驱动设计)架构的时候,发现up使用的mapstruct做的实体之间的转换,后面也了解了一下,发现这个工具还是很优秀的。

二、Mapstruct

MapStruct 是一个代码生成器,用于创建实现Java Bean之间转换的扩展映射器,它基于约定优于配置的方法极大地简化了 Java bean 之间映射的实现,我们只需要创建接口,MapStruct就会在编译时自动创建一个具体的实现进行对象的转换

2.1、Mapstruct和BeanUtil的比较 

Mapstruct的性能远远高于BeanUtils,这应该是大佬使用Mapstruct的主要原因,下面是我的测试结果,可以看出随着属性个数的增加,BeanUtils的耗时也在增加,并且BeanUtils的耗时跟属性个数成正比,而Mapstruct的耗时却一直是1秒,所以从对比数据可以看出Mapstruct是非常优秀的,其性能远远超过BeanUtil


BeanUtils 只能同属性映射,或者在属性相同的情况下,允许被映射的对象属性少;但当遇到被映射的属性数据类型被修改或者被映射的字段名被修改,则会导致映射失败。

 

2.2、优势,为什么选择Mapstruct

2.3、快速入门

2.3.1、引入依赖

 <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.4.2.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.4.2.Final</version>
        </dependency>

2.3.1、定义DTO和BO 

/**
 * SubjectCategoryDTO
 */
@Data
public class SubjectCategoryDTO implements Serializable {
    private static final long serialVersionUID = -36288445272926615L;
    private Long id;
    private String categoryName;
    private Integer categoryType;
    private String imageUrl;
    private Long parentId;
    private Integer count;

}
@Data
public class SubjectCategoryBO implements Serializable {
    private static final long serialVersionUID = -36288445272926615L;
    private Long id;
    private String categoryName;
    private Integer categoryType;
    private String imageUrl;
    private Long parentId;
    private Integer count;
}

2.4.1、定义Convert接口

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

    //将DTO转换为BO
    SubjectCategoryBO convertDTOToBO(SubjectCategoryDTO subjectCategoryDTO);

    //将List<BO>转换为List<DTO>
    List<SubjectCategoryDTO> convertBoListToDTOList(List<SubjectCategoryBO> subjectCategoryBOList);

    //将BO转换为DTO
    SubjectCategoryDTO convertBoToCategoryDTO(SubjectCategoryBO subjectCategoryBO);
}

2.4、性能好的原因

Java程序执行的过程,是由编译器先把java文件编译成class字节码文件,然后由JVM去解释执行class文件。Mapstruct正是在java文件到class这一步帮我们实现了转换方法,即做了预处理,提前编译好文件,如果用过lombok的同学一定能理解其好处,通过查看class文件,可以看出SubjectCategoryDTOConvert 被打上org.mapstruct.Mapper注解后,编译器自动会帮我们生成一个实现类SubjectCategoryDTOConvertImpl,并实现了convertDTOToBO、convertBoListToDTOList、convertBoToCategoryDTO这些方法

 

从生成的代码可以看出,转化过程非常简单,只使用了UserPo的get方法和UserEntity的set方法,没有复杂的逻辑处理,清晰明了,所以性能很高

 相对于BeanUtils来说

BeanUtils,转换的原理是使用的反射,反射的效率相对来说是低的,因为jvm优化在这种场景下有可能无效,所以在对性能要求很高或者经常被调用的程序中,尽量不要使用。我们平时在研发过程中,也会遵守这个原则,非必要,不反射。


从下面的BeanUtils的copyProperties方法代码中可以看出,转化逻辑非常复杂,有很多的遍历,去获取属性,获取方法,设置方法可访问,然后执行,所以执行效率相对Mapstruct来说,是非常低的。回头看Mapstruct自动生成的实现类,简洁、高效

 BeanUtils的copyProperties方法的源码:

 private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) throws BeansException {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        if (editable != null) {
            if (!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
            }

            actualEditable = editable;
        }

        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
        PropertyDescriptor[] var7 = targetPds;
        int var8 = targetPds.length;

        for(int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null) {
                        ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
                        ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
                        if (targetResolvableType.isAssignableFrom(sourceResolvableType)) {
                            try {
                                if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                    readMethod.setAccessible(true);
                                }

                                Object value = readMethod.invoke(source);
                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                    writeMethod.setAccessible(true);
                                }

                                writeMethod.invoke(target, value);
                            } catch (Throwable var17) {
                                throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var17);
                            }
                        }
                    }
                }
            }
        }

    }

所以综上所述,

Mapstruct的高性能是毋庸置疑的,这也是我选择使用他的根本原因。在使用方式上和BeanUtils对比,Mapstruct需要创建mapper接口和自定义转换工具类,其实上手成本并不高,但是我们换取了高性能,这是非常值得的,所以强烈推荐大家使用Mapstruct,是时候和BeanUtils说再见了

对于 Mapstruct更深入的学习,大家可以自己自行搜索学习~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1866726.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

对https://registry.npm.taobao.org/tyarn的请求失败,原因:证书过期

今天安装yarn时&#xff0c;报错如下&#xff1a; request to https://registry.npm.taobao.org/yarn failed, reason: certificate has expired 原来淘宝镜像过期了&#xff0c;需要重新搞一下 记录一下解决过程&#xff1a; 1.查看当前npm配置 npm config list 2.清…

Vite: 集成Lint工具规范代码

概述 在前端开发中&#xff0c;尤其是在大型项目中&#xff0c;代码的规范性和一致性对于项目的可维护性、可读性以及团队协作效率至关重要。为了保障代码质量&#xff0c;前端社区涌现出了许多Lint工具&#xff0c;如ESLint、Prettier、Stylelint等&#xff0c;它们能帮助我们…

高考成绩加分,西藏学生推荐使用的《藏文翻译词典》APP,藏文作文高考大纲,初中高中学习内容与考试同步更新!

2024年高考成绩出炉啦&#xff01;在这个特别的时刻&#xff0c;我想向大家表达最真挚的祝贺。高考不仅是一场考试&#xff0c;更是你多年学习旅程的一次总结。当你的成绩揭晓&#xff0c;无论结果如何&#xff0c;你都应该为自己感到骄傲。 在高原&#xff0c;藏语如同雪山上…

切线与切平面的可视化

切线与切平面的可视化 flyfish 切线的可视化 import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation, PillowWriter# 定义一个简单的一元函数&#xff0c;例如 f(x) x^2 def func(x):return x**2# 计算函数的导数 def deriva…

鸿蒙开发Ability Kit(程序框架服务):【FA模型切换Stage模型指导】 app和deviceConfig的切换

app和deviceConfig的切换 为了便于开发者维护应用级别的属性配置&#xff0c;Stage模型将config.json中的app和deviceConfig标签提取到了app.json5中进行配置&#xff0c;并对部分标签名称进行了修改&#xff0c;具体差异见下表。 表1 配置文件app标签差异对比 配置项FA模型…

MindSpore中NumPy变量转换为Tensor张量使用的Tensor.from_numpy()函数到底是深拷贝还是浅拷贝

在NumPy转换为Tensor使用的Tensor.from_numpy()函数到底是深拷贝还是浅拷贝 使用Tensor()将NumPy变量转换为Tensor变量。 类似数组转换张量的方法 n np.ones(5) t Tensor.from_numpy(n) print(f"t: {t}", type(t)) np.add(n, 1, outn) print(f"n: {n}"…

导航栏设计的5种类型,新手不容忽视的重要知识!

导航栏是网页设计中不可缺少的一部分。大多数用户在浏览网页时都是从导航栏开始的。导航栏的作用相当于路标和书籍中的目录&#xff0c;其重要性不言而喻。从设计的角度来看&#xff0c;网页导航栏的设计功能大于视觉效果。因此&#xff0c;网页导航栏的设计可以分为 5 类型&am…

Java后端 || ElementUI 显示后端树形表格数据

文章目录 1、前端源码2、数据库设计3、后端设计3.1、实体类3.2、Controller层3.3、具体树形列表后端代码实现 1、前端源码 ElementUI Table 链接 在此链接中找到 树形数据与懒加载 查看其JS源码&#xff0c;可知&#xff0c;每个菜单节点的子节点存放于children字段中&#x…

直播分享|TinyVue 组件库主题适配原理与实战

在前端开发过程中&#xff0c;不同的项目可能需要不同的设计风格。而了解组件库的主题适配功能&#xff0c;也可以帮助开发者轻松定制独特的主题风格&#xff0c;从而满足各种设计需求。因此6月27日晚19点&#xff0c;体验技术团队 TinyVue 项目成员岑灌铭老师将为大家带来以《…

▶《强化学习的数学原理》(2024春)_西湖大学赵世钰 Ch5 蒙特卡洛方法【model-based ——> model-free】

PPT 截取必要信息。 课程网站做习题。总体 MOOC 过一遍 1、视频 学堂在线 习题 2、 过 电子书 是否遗漏 【下载&#xff1a;本章 PDF GitHub 页面链接 】 【第二轮 才整理的&#xff0c;忘光了。。。又看了一遍视频】 3、 过 MOOC 习题 看 PDF 迷迷糊糊&#xff0c; 恍恍惚惚。…

Swift 周报 第五十五期

文章目录 前言新闻和社区苹果公司据悉将推出密码管理应用三大指数涨跌不一&#xff0c;苹果重新夺回美股第二大上市公司宝座苹果iOS 18新动向&#xff1a;AI功能强化隐私保护&#xff0c;用户自主选择启用 提案通过的提案正在审查的提案 Swift论坛推荐博文话题讨论关于我们 前言…

springboot + Vue前后端项目(第十九记)

项目实战第十九记 写在前面1. redis安装(windows安装)1.1 获取软件链接地址&#xff1a;1.2 启动redis1.3 测试是否启动成功1.4 通过 Another Redis DeskTop软件可视化查看redis 2. SpringBoot集成redis2.1 引入依赖2.2 注入RedisTemplate2.3 使用redis2.4 redis更新2.5 redis使…

【经验分享】Claude3.5 Sonnet六大可扩展用途

Claude3.5 Sonnet六大可扩展用途 概述 Claude 3.5 Sonnet的性能比其他大模型都有好&#xff0c;本文基于Claude3.5 Sonnet的Artifact功能进行讨论和分析&#xff0c;提供了Claude3.5 Sonnet的六大可扩展用途。 用途 1.画SVG图像 2.设计网站 3.设计徽标 4.设计游戏 5.分…

JavaScript的学习之dom的查询(一)

一、获得元素 通过document对象调用&#xff1a; getElementById()&#xff1a;通过id属性获取一个元素节点对象getElementsByTagName()&#xff1a;通过标签名获取一组元素节点对象getElementsByName()&#xff1a;通过name属性来获取一组元素节点对象 核心学习代码 <scrip…

【语义分割】1-标注数据集-【单张图片】labelme标注json文件转mask

声明&#xff1a;我学习了b站&#xff1a;标注自己的语义分割数据集_哔哩哔哩_bilibili 并且复现了&#xff0c;记录了所思所得。 主要是说了&#xff1a; 做语义分割&#xff0c;数据集怎么用labelme标注成json文件&#xff0c;以及&#xff0c;json文件怎么转成mask 流程…

springcould-config git源情况下报错app仓库找不到

在使用spring config server服务的时候发现在启动之后的一段时间内控制台会抛出异常&#xff0c;spring admin监控爆红&#xff0c;控制台信息如下 --2024-06-26 20:38:59.615 - WARN 2944 --- [oundedElastic-7] o.s.c.c.s.e.JGitEnvironmentRepository : Error occured …

Linux - 记一次某Java程序启动报错(申请内存失败)

文章目录 问题可能原因分析可能原因分析尝试各种解决方案尝试解决过程 解决办法&#xff1a; 调整 overcommit_meory参数overcommit_memory详解什么是 overcommit_memory&#xff1f;overcommit_memory 的选项及其含义配置 overcommit_memory查看当前设置设置 overcommit_memor…

如何为您的企业实施营销翻译

要为您的业务实施营销翻译&#xff0c;您需要执行以下步骤&#xff1a; 评估市场需求 你首先需要做的事情之一是确定你的产品或服务可以销售的地区或国家。这可以通过 看看在这些地区已经取得成功的竞争对手阅读行业的市场调查和预测&#xff0c;了解市场趋势和潜在机会评估…

经销or直营,新老车企殊途终要同归

步入2024&#xff0c;汽车圈的新人、老人都逐渐活成了对方的样子。 2024年5月&#xff0c;先后有消息传来&#xff0c;阿维塔要从直营模式全部转向经销模式&#xff0c;蔚来新发布的子品牌“乐道”未来可能考虑合作或加盟&#xff0c;并单独建设门店。 而就在5月1日&#xff…

网工内推 | 国企信息工程师,信息系统项目管理师优先,最高14薪

01 上海浦东软件园股份有限公司 &#x1f537;招聘岗位&#xff1a;信息化管理工程师 &#x1f537;岗位职责&#xff1a; 1. 根据公司战略、数字化总体架构规划和IT 技术趋势&#xff0c;制定信息化系统的规划与设计&#xff0c;并制定实施计划。 2. 统筹公司信息化系统管理…