可别再用BeanUtils了(性能拉胯),试试这款转换神器

老铁们是不是经常为写一些实体转换的原始代码感到头疼,尤其是实体字段特别多的时候。有的人会说,我直接使用get/set方法。没错,get/set方法的确可以解决,而且也是性能较高的处理方法,但是大家有没有想过,要是转换的实体字段比较多,那么get/set方法的代码量是非常恐怖的;有人会说,用自带的BeanUtils工具类,但在属性拷贝的过程中,会遇到类型转换异常,或是影响性能等等问题。所以,我把自己项目中使用的这款转换神器分享出来。BeanUtils属性复制

之前对各种属性映射工具的性能进行了简单的对比,结果如下:

先贴下官网地址吧:https://mapstruct.org/

一、pom 配置

    <!-- 属性设置 -->
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <mybatisPlus.version>3.5.3.1</mybatisPlus.version>
        <lombok.version>1.18.26</lombok.version>
        <mysql.version>8.0.30</mysql.version>
        <alibaba.druid.version>1.2.4</alibaba.druid.version>
        <thymeleaf.version>3.0.15.RELEASE</thymeleaf.version>
        <spring4.version>3.0.12.RELEASE</spring4.version>
        <mapstruct.version>1.5.3.Final</mapstruct.version>
    </properties>

    <!-- 依赖关系 -->
    <dependencies>
        <!-- springmvc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 测试test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- jpa(持久层) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!--mybatis-plus依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatisPlus.version}</version>
        </dependency>
        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <!-- mysql(数据库) -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!-- alibaba数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${alibaba.druid.version}</version>
        </dependency>

        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>${thymeleaf.version}</version>
        </dependency>

        <!-- Spring 4集成包(这可能是Spring应用程序所需的一切)-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring4</artifactId>
            <version>${spring4.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 属性转换 -->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>

        <!-- idea 2018.1.1 之前的版本需要添加下面的配置,后期的版本就不需要了,可以注释掉,我自己用的2022.2.3 -->
        <!--  <dependency>-->
        <!--     <groupId>org.mapstruct</groupId>-->
        <!--     <artifactId>mapstruct-processor</artifactId>-->
        <!--     <version>${org.mapstruct.version}</version>-->
        <!--     <scope>provided</scope>-->
        <!--  </dependency>-->

    </dependencies>

    <!-- 编译插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>
            <!-- 原有工具自带的编译器时无法生成实现类,需要maven 的方式来进行编译,才会生成属性转换的实现类。需添加以下依赖 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <annotationProcessorPaths>
                    <!-- lombok的依赖位置一定放在mapstruct之前 否则转换后的对象获取不到属性值 -->
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

PS:lombok和mapstruct的版本兼容问题(可以直接使用上面pom里的版本)

1、maven插件要使用3.6.0版本以上;

2、lombok使用1.16.16版本以上;

3、另外编译的lombok和mapstruct的插件不要忘了加上。否则会出现下面的错误:No property named "aaa" exists in source parameter(s). Did you mean "null"?

二、转换实体类

⑴.源实体1:

package com.***;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * 学生
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    /*** ID*/
    private Long userId;

    /*** 姓名*/
    private String name;

    /*** 年龄*/
    private Integer age;

    /*** 手机号*/
    private String phone;

    /*** 性别(1男 2女)*/
    private Integer sex;

    /*** 生日*/
    private String birthday;

    /*** 创建时间*/
    private Date createTime;

}

⑵.源实体2:

package com.***;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 课程
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Course {

    /*** 课程ID*/
    private Long courseId;

    /*** 课程名称*/
    private String courseName;

    /*** 序号*/
    private Integer sortNo;

}

⑶.目标实体1:

package com.***;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * 学生Vo
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVo {

    /***用户ID */
    private Long userId;

    /*** 姓名*/
    private String name;

    /*** 年龄*/
    private Integer age;

    /*** 手机号*/
    private String phone;

    /*** 性别(1男 2女)*/
    private Integer gender;

    /*** 生日*/
    private String birthday;

    /*** 创建时间*/
    private Date createTime;

}

⑷.目标实体2:

package com.***;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 学生课程Vo
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentCourseVo {

    /*** 学生姓名*/
    private String name;

    /*** 年龄*/
    private Integer age;

    /*** 性别(1男 2女)*/
    private String gender;

    /*** 生日*/
    private String birthday;

    /*** 课程名称*/
    private String course;

}

⑸.转换Mapper接口:(编译完成后会自动生成相应的实现类)

package com.***;

import com.test.java.domain.entity.Course;
import com.test.java.domain.entity.Student;
import com.test.java.domain.vo.StudentCourseVo;
import com.test.java.domain.vo.StudentVo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 转换mapper
 */
@Mapper
public interface StudentWrapper {

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

    // 转换一:实体转实体
    @Mapping(target = "userId", ignore = true)
    @Mapping(source = "sex", target = "gender")
    @Mapping(target = "createTime", expression = "java(new java.util.Date())")
    @Mapping(source = "name", target = "name", defaultValue = "张三")
    StudentVo convertVo(Student student);

    // 转换二:list转list
    List<StudentVo> convertList(List<Student> list);

    // 转换三:多对象转换一个对象
    @Mapping(source = "student.sex", target = "gender")
    @Mapping(source = "course.courseName", target = "course")
    StudentCourseVo convertTo(Student student, Course course);

}

现在就可以直接使用mapper接口进行属性转换了,以下举例有三种转换用法:

1、转换一:对象转对象

2、转换二:集合转集合

3、转换三:多个对象转单一对象

    public static void main(String[] args) {
        // 转换一
        Student student = Student.builder().userId(100L).age(18).phone("13012345678").sex(1).birthday("01-01").build();
        StudentVo convertVo = StudentWrapper.INSTANCE.convertVo(student);
        System.out.println("转换一、实体转实体:" + convertVo);

        // 转换二
        Student student1 = Student.builder().userId(1L).name("张三").age(18).phone("13012345678").sex(1).birthday("01-01").createTime(new Date()).build();
        Student student2 = Student.builder().userId(2L).name("李四").age(20).phone("15026668652").sex(2).birthday("05-01").createTime(new Date()).build();
        List<Student> list = new ArrayList<>();
        list.add(student1);
        list.add(student2);
        List<StudentVo> convertList = StudentWrapper.INSTANCE.convertList(list);
        System.out.println("转换二、list转list:" + convertList);

        // 转换三
        Student student3 = Student.builder().name("小明").age(18).phone("13512365568").sex(1).birthday("05-06").createTime(new Date()).build();
        Course course = Course.builder().courseName("《Java从入门到放弃》").sortNo(1).build();
        StudentCourseVo convertTo = StudentWrapper.INSTANCE.convertTo(student3, course);
        System.out.println("转换三、多对象转换一个对象:" + convertTo);
    }

转换结果:

三、特殊处理

mapper转换接口中,还可以进行字段映射,改变字段类型,指定格式化、赋予默认值、过滤不需要转换的字段、自定义转换等方式,包括一些日期的默认处理,只需在@Mapping注解中添加相应参数即可,如下:

  • 字段映射
    @Mapping(source = "sex", target = "gender")
    StudentVo convertVo(Student student);
source:代表源字段;
target:代表目标字段;
  • 日期格式化
    @Mapping(source = "createTime",target = "createTime",dateFormat = "yyyy-MM-dd HH:mm:ss")
    StudentVo convertVo(Student student);
  • 默认值
    @Mapping(source = "name", target = "name", defaultValue = "张三")
    StudentVo convertVo(Student student);

 defaultValue:转换时,赋予默认值;

  • 过滤不需要转换的字段
    @Mapping(target = "userId", ignore = true)
    StudentVo convertVo(Student student);

ignore:默认为false,不过滤;

  • 自定义转换
    @Mapping(target = "createTime", expression = "java(new java.util.Date())")
    StudentVo convertVo(Student student);

expression:自定义转换规则,当前案例是createTime字段获取当前系统时间

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构与算法——堆的基本存储

目录 一、概念及其介绍 二、适用说明 三、结构图示 四、Java 实例代码 五.堆和栈的区别 一、概念及其介绍 堆(Heap)是计算机科学中一类特殊的数据结构的统称。 堆通常是一个可以被看做一棵完全二叉树的数组对象。 堆满足下列性质&#xff1a; 堆中某个节点的值总是不大…

MySQL主从复制

主从复制概述 如何提升数据库并发能力 在实际工作中&#xff0c;我们常常将 Redis 作为缓存与 MySQL 配合来使用&#xff0c;当有请求的时候&#xff0c;首先会从缓存中进行查找&#xff0c;如果存在就直接取出。如果不存在再访问数据库&#xff0c;这样就 提升了读取的效率&…

I2C和SPI总线以及通信

通讯属性 概括 Serial/parallel 串行/并行Synchronous/asynchronous 同步/异步Point-to-point / bus 点对点 总线Half-duplex/full-duplex 半双工/全双工Master-slave/ equal partners 主从/对等single-ending / differential 单端/差分 点对点和总线 点对点通讯 只有两个通…

【简陋Web应用2】人脸检测——基于Flask和PaddleHub

文章目录&#x1f6a9; 前言&#x1f33a; 效果演示&#x1f966; 分析与设计&#x1f349; 实现&#x1f36c; 1. 部署人脸检测模型&#x1f36d; 2. 使用Flask构建app2.1 目录结构2.2 forms.py2.3 utils.py2.4 app.py2.5 index.html&#x1f95d; Bug(s)&#x1f6a9; 前言 本…

V2G模式下含分布式能源网优化运行研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f4cb;&#x1f4cb;&#x1f4cb;本文目录如下&#xff1a;&#x1f381;&#x1f381;&#x1f381; 目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &am…

手写一个简单的RPC框架

学习RPC框架&#xff0c;由繁化简&#xff0c;了解其本质原理 文章目录项目简介什么是RPC&#xff1f;项目模块项目代码common模块client模块server模块framework模块测试项目简介 什么是RPC&#xff1f; RPC&#xff08;Remote Procedure Call&#xff09;即远程过程调用&am…

Cursor:GPT-4 驱动的强大代码编辑器

Cursor &#xff08;https://www.cursor.so/&#xff09;是 GPT-4 驱动的一款强大代码编辑器&#xff0c;可以辅助程序员进行日常的编码。下面通过一个实际的例子来展示 Cursor 如何帮助你编程。这个例子做的事情是网页抓取。抓取的目标是百度首页上的百度热搜&#xff0c;如下…

SWA Object Detection随机权重平均【论文+代码】

随机权重平均摘要IntroductionSWA实验部分消融实验摘要 您想在不增加推断成本和不改变检测器的情况下提高对象检测器的1.0 AP吗&#xff1f;让我们告诉您一个这样的秘方。这个秘方令人惊讶地简单&#xff1a;使用循环学习率训练您的检测器额外的12个epoches&#xff0c;然后将…

最强的Python可视化神器,你有用过么?

数据分析离不开数据可视化&#xff0c;我们最常用的就是Pandas&#xff0c;Matplotlib&#xff0c;Pyecharts当然还有Tableau&#xff0c;看到一篇文章介绍Plotly制图后我也跃跃欲试&#xff0c;查看了相关资料开始尝试用它制图。 1、Plotly Plotly是一款用来做数据分析和可视…

【数据结构】Java实现队列与循环队列

目录 1. 概念 2. 队列的使用 3. 自己动手实现队列 3.1 MyQueue接口 3.2 LinkedQueue类 3.3 入队列 3.4 出队列 3.5 获取队头元素 3.6 获取队列中有效元素个数与检测队列是否为空 3.7 toString方法 4. 整体实现 4.1 LinkedQueue类 4.2 Test类 4.3 测试结果 5. 循…

while实现1到100相加求和-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第2章-课后作业)

【案例2-7】while实现1到100相加求和 一、案例描述 考核知识点 while循环语句 练习目标 掌握while循环语句。 需求分析 1-100之间的数相加求和&#xff0c;本案例通过while循环语句来实现。 案例分析 效果如图2-10所示。1-100所有数的和 具体实现步骤如下&#xff1a; 在&l…

【进阶数据结构】——红黑树

&#x1f308;感谢阅读East-sunrise学习分享——[进阶数据结构]红黑树 博主水平有限&#xff0c;如有差错&#xff0c;欢迎斧正&#x1f64f;感谢有你 码字不易&#xff0c;若有收获&#xff0c;期待你的点赞关注&#x1f499;我们一起进步&#x1f680; &#x1f308;我们上一…

SpringCloud之 LoadBalancer和Feign负载均衡

文章目录LoadBalancer 负载均衡一、LoadBalanced 负载均衡&#x1f33d;①观察负载均衡现象&#x1f33d;②LoadBalanced 源码剖析二、自定义负载均衡三、OpenFeign 实现负载均衡&#x1f346;①添加依赖&#x1f346;②启动类添加 EnableFeignClients&#x1f346;③创建客户端…

MySQL的COUNT语句,竟然都能被面试官虐的这么惨!?

关于数据库中行数统计&#xff0c;无论是MySQL还是Oracle&#xff0c;都有一个函数可以使用&#xff0c;那就是COUNT 但是&#xff0c;就是这个常用的COUNT函数&#xff0c;却暗藏着很多玄机&#xff0c;尤其是在面试的时候&#xff0c;一不小心就会被虐。不信的话请尝试回答下…

一文了解Jackson注解@JsonFormat及失效解决

背景 项目中使用WRITE_DATES_AS_TIMESTAMPS: true转换日期格式为时间戳未生效。如下&#xff1a; spring:jackson:time-zone: Asia/Shanghaiserialization:WRITE_DATES_AS_TIMESTAMPS: true尝试是否关于时间的注解是否会生效&#xff0c;使用JsonForma和JsonFiled均失效。 常…

【Docker】CAdvisor+InfluxDB+Granfana容器监控

文章目录原生命令 docker stats容器监控3剑客CIGCAdvisorInfluxDBGranfanacompose容器编排&#xff0c;一套带走新建目录新建3件套组合的 docker-compose.yml检查配置&#xff0c;有问题才有输出 docker-compose config -q启动docker-compose文件 docker-compose up -d测试浏览…

HTML5 Canvas

HTML5 Canvas <canvas>元素是HTML5中的新元素&#xff0c;通过使用该元素&#xff0c;你可以在网页中绘制所需的图形。 标签定义图形&#xff0c;比如图表和其他图像&#xff0c;您必须使用脚本来绘制图形。在画布上&#xff08;Canvas&#xff09;画一个红色矩形&#…

Java基础知识之HashMap的使用

一、HashMap介绍 HashMap是Map接口的一个实现类&#xff08;HashMap实现了Map的接口&#xff09;&#xff0c;它具有Map的特点。HashMap的底层是哈希表结构。 Map是用于保存具有映射关系的数据集合&#xff0c;它具有双列存储的特点&#xff0c;即一次必须添加两个元素&#xf…

5个高清/4K视频素材网站,免费下载。

本期跟大家分享5个超好用的视频素材网站&#xff0c;4K质量&#xff0c;免费可商用。 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYwNDUx 菜鸟图库主要提供设计素材为主&#xff0c;自媒体相关素材也很多&#xff0c;像商用图片、背景图、视频素材、音频素材都很齐…

算法刷题总结 (二) 回溯与深广搜算法

算法总结2 回溯与深广搜算法一、理解回溯算法1.1、回溯的概念1.2、回溯法的效率1.3、回溯法问题分类1.4、回溯法的做题步骤二、经典问题2.1、组合问题2.1.1、77. 组合 - 值不重复2.1.2、216.组合总和III - 值不重复且等于目标值2.1.3、17. 电话号码的字母组合 - 双层回溯2.1.4、…