高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版
一、前言
大家在开发中,最让人头疼的就是:对象之间的拷贝,前端的VO和数据库的Entity不一致!
性能最好的就是手动set,主要是枯燥且无技术含量,不仅耗费大量时间而且很容易出错;
所以我们要成为优秀的程序员,要多借助轮子,开发效率事半功倍,开发技能也是增长不少!
如果系统性能没有要求,怎么实现都是好的,但是我们要有追求哈,追求高质量!
每个东西都有存在的价值,不要捧一踩一哈!
二、MapStruct简介
MapStruct是基于JSR 269的Java注释处理器,用于生成类型安全的 Bean 映射类。
您所要做的就是定义一个映射器接口,该接口声明任何所需的映射方法。在编译过程中,MapStruct将生成此接口的实现。此实现使用纯 Java 方法调用在源对象和目标对象之间进行映射,即无反射或类似内容。
与手动编写映射代码相比,MapStruct通过生成繁琐且容易出错的代码来节省时间。遵循配置方法的约定,MapStruct使用合理的默认值,但在配置或实现特殊行为时会步入歧途。

三、优势
与动态映射框架相比,MapStruct具有以下优点:
通过使用普通方法调用而
不是反射快速执行编译时类型安全:只能映射彼此映射的对象和属性,不会意外地将订单实体映射到客户 DTO 等。在构建时清除错误报告,如果
映射不完整(并非所有目标属性都已映射)
映射不正确(找不到正确的映射方法或类型转换)
性能图大家可以看一下:

四、整合实战
0. 使用
@Mapper将接口标记为映射接口
对于源对象和目标对象中具有不同名称的属性,可以使用注释来配置名称:@Mapping
按照约定,接口声明一个成员Mappers INSTANCE,为客户端提供对映射器实现的访问。
下面我们来具体使用!
1. 导入依赖
这里使用最新的,如果引入了lombok可能会有问题,就是他们俩都是在编译期运行的,mapstruct如果比lombok先执行,就会找不到get、set方法,所以会有问题,官网已经有了解决方案!现在是启动不会报错!
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
2. 错误总结
- 不会自动生成impl实现类?
我们需要加上依赖:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
</dependency>
- 重新启动就会出现和lombok的冲突问题:
java: No property named "name" exists in source parameter(s).
Type "UserVO" has no properties.
<build>
<pluginManagement>
<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>1.5.3.Final</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
3. 常用实战1
用户表:
@Data
public class User {
private Integer id;
private String username;
private Integer age;
}
前端用户VO:
@Data
public class UserVO {
private Integer id;
private String name;
private Integer age;
}
我们创建接口进行两个对象之间的映射:
import com.example.demo.mapstruct.entity.User;
import com.example.demo.mapstruct.entity.UserVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @author wangzhenjun
* @date 2023/1/28 16:05
*/
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(source ="name",target = "username")
User userVOToUser(UserVO userVO);
}
属性多了可以嵌套:
@Mappings({
@Mapping(source ="name",target = "username"),
@Mapping(source ="name1",target = "username1")
})
也可以:
@Mapping(source ="name",target = "username")
@Mapping(source ="name1",target = "username1")
编写测试类:
@SpringBootTest
class DemoApplicationTests {
@Test
void demoMapstruct(){
UserVO userVO = new UserVO(1,"小红",18);
User user = UserMapper.INSTANCE.userVOToUser(userVO);
System.out.println(user);
}
}

我们看到拷贝没有任何问题!
我们看看是怎么实现的:
mapstruct会在编译期自动生成实现类去帮助我们去赋值,不指定位默认策略,名称一致进行copy!
不一致可以按上面的进行指定,不指定则不会有set方法!

4. 常用实战2
下面进行多个源参数的映射方法演示:
我们把user类加上一个字段:
private BigDecimal score;
新增加一个Score类:
@Data
@AllArgsConstructor
public class Score {
private Integer studentId;
private BigDecimal score;
}
调整上面的UserMapper接口:
@Mappings({
@Mapping(source ="userVO.name",target = "username"),
@Mapping(source ="score.score",target = "score")
})
User userVOToUser(UserVO userVO, Score score);
测试代码:
UserVO userVO = new UserVO(1,"小红",18);
Score score = new Score(1,new BigDecimal(100));
User user = UserMapper.INSTANCE.userVOToUser(userVO,score);
System.out.println(user);
结果显示正常:

5. 常用实战3
我们在来看一个企业级能够用得上的,就是自定义方法,然后进行赋值:
场景:一个商品有长宽高,我们把长宽高从cm变为m!
还有很多String转Integer、Float等等,都是按照下面这种自定义方法去实现!
VO和对象都是一样的哈!
@Data
@AllArgsConstructor
public class ProductVO {
private Integer id;
private String name;
private BigDecimal length;
private BigDecimal width;
private BigDecimal high;
}
看清楚,别导错包了!
qualifiedByName:指定自定义方法的名称
@Named("cmToM"):起别名,不使用找不到方法
可以写一起,也可以整一个工具类里写方法,在这里进行引用!
如果是使用spring,我们可以把接口作为bean进行注入调用(推荐)
加上参数即可开启:
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
/**
* @author wangzhenjun
* @date 2023/1/28 17:13
*/
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface ProductMapper {
@Mapping(source = "length",target = "length",qualifiedByName = "cmToM")
@Mapping(source = "width",target = "width",qualifiedByName = "cmToM")
@Mapping(source = "high",target = "high",qualifiedByName = "cmToM")
Product productVOToPrduct(ProductVO productVO);
@Named("cmToM")
default BigDecimal cmToM (BigDecimal oldValue){
if (oldValue == null) {
return BigDecimal.ZERO;
}
return oldValue.divide(new BigDecimal("100"));
}
}
测试:
@SpringBootTest
class DemoApplicationTests {
@Autowired
private ProductMapper productMapper;
@Test
void demoMapstruct(){
ProductVO productVO = new ProductVO(1,"美丽家园地板",new BigDecimal(100),new BigDecimal(50),new BigDecimal(8));
Product product = productMapper.productVOToProduct(productVO);
System.out.println(product);
}
}
完美转化!

6. 常用实战4
在实战一个LocalDateTime、String相互转化的,后面大家可以去官网文档去找你需要的:
在刚刚的商品类加个字段:
private String createdAt;
VO里也加上一个:
private LocalDateTime createdAt;
编写个转化类:
这里交给spring管理了,因为ProductMapper也交给spring管理,不加的话会找不到此类!
@Component
public class LocalDateTimeMapper {
public String asString(LocalDateTime date) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return date != null ? date.format(formatter): null;
}
public LocalDateTime asLocalDateTime(String date) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return date != null ? LocalDateTime.parse(date,formatter) : null;
}
}
ProductMapper修改一下:
uses = LocalDateTimeMapper.class使用咱们上面写的类即可!
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING,uses = LocalDateTimeMapper.class)
public interface ProductMapper {
@Mapping(source = "length",target = "length",qualifiedByName = "cmToM")
@Mapping(source = "width",target = "width",qualifiedByName = "cmToM")
@Mapping(source = "high",target = "high",qualifiedByName = "cmToM")
Product productVOToProduct(ProductVO productVO);
@Named("cmToM")
default BigDecimal cmToM (BigDecimal oldValue){
if (oldValue == null) {
return BigDecimal.ZERO;
}
return oldValue.divide(new BigDecimal("100"));
}
}
测试一下:
ProductVO productVO = new ProductVO(1,"美丽家园地板",
new BigDecimal(100),new BigDecimal(50),
new BigDecimal(8), LocalDateTime.now());
Product product = productMapper.productVOToProduct(productVO);
System.out.println(product);
完美转化:

六、总结
通过简介到实战,这时咱们就是优雅的程序员了!
更多的例子可以去官网进行查看:
写作不易,大家给点支持,你的支持是我写作的动力哈!
对你有帮助,还请不要吝啬你的发财小手点点关注哈!
关注小编的微信公众号,一起交流学习!文章首发看哦!

高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版的更多相关文章
- CSS+DIV网页样式布局实战从入门到精通 中文pdf扫描版
CSS+DIV网页样式布局实战从入门到精通通过精选案例引导读者深入学习,系统地介绍了利用CSS和DIV进行网页样式布局的相关知识和操作方法. 全书共21章.第1-5章主要介绍网页样式布局的基础知识,包 ...
- CentOS7入门到精通实战课程课后习题
Linux自动化运维系列①: CentOS7入门到精通实战--->传送门 http://edu.51cto.com/course/13055.html 01.系统入门课后习题 1.口述一个命令执 ...
- 《C#从入门到精通(第3版)》目录
C#从入门到精通(第3版)pdf+源码 一.基础知识 1.初识C#及其开发环境 2.开始C#之旅 3.变量与常量 4.表达式与运算符 5.字符与字符串 6.流程控制语句 7.数组与集合 8.属性和方法 ...
- Python从入门到精通(第2版)——pyuic5: error: no such option: -m的问题解决
前言 在学习<Python从入门到精通(第2版)>的第15章 GUI界面编程--15.2.4 将.ui文件转换为.py文件时,按照书中步骤出错时的问题解决,希望对同样学习本书的同学有所帮助 ...
- 优雅的对象转换解决方案-MapStruct及其入门(一)
第一次看到 MapStruct 的时候, 我个人非常的开心. 因为其跟我内心里面的想法不谋而合. 1 MapStruct 是什么? 1.1 JavaBean 的困扰 对于代码中 JavaBean之间的 ...
- Scala2.12 从入门到精通实战高端视频课程(含网盘下载地址)
Scala快速入门到精通 下载地址链接:https://pan.baidu.com/s/1bTSZSlWftFYaLQL6lVH62A 提取码:ohfk 下载后使用视频中自带的专用播放器打开视频就能看 ...
- 优雅的对象转换解决方案-MapStruct使用进阶(二)
在前面, 介绍了 MapStruct 及其入门. 本文则是进一步的进阶. 在 MapStruct 生成对应的实现类的时候, 有如下的几个情景. 1 属性名称相同,则进行转化 在实现类的时候, 如果属性 ...
- Echarts轻松入门,内附踩坑秘籍
首先介绍一下我们的主角ECharts ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Fir ...
- MySQL5.7从入门到精通 (视频教学版) 刘增杰 编著
第1章 初识MySQL MySQL是一个开放源代码的数据库管理系统(DBMS),它是由MySQL AB公司开发.发布和支持的.MySQL是一个跨平台(Windows.Linux.UNIX.MacOS) ...
- 《PHP从入门到精通(第3版)》目录
一.基础知识 1.初识PHP 2.PHP环境搭建和开发工具 3.PHP语言基础 4.流程控制语句 5.字符串操作 6.正则表达式 7.PHP数组 8.PHP与Web页面交互 9.PHP与JavaScr ...
随机推荐
- vue3 结合 element-plus 框架实现增删改查功能(不连接数据库)
一.效果图 二.代码 2.1.导入依赖(已经安装过node.js) npm install element-plus --save 注意:要是安装失败,可以使用淘宝镜像进行下载,如下: 2.2.设置淘 ...
- 第一章:TypeScript快速入门
一.TypeScript 开发环境搭建 1.TypeScript 是什么? TypeScript 是一种由微软开发的自由和开源的编程语言.它是 JavaScript 的一个超集,而且本质上向这个语言添 ...
- Codeforces Round #835 (Div. 4) A-G
比赛链接 A 题意 给出三个不同的数,求中位数. 题解 知识点:模拟. 显然. 时间复杂度 \(O(1)\) 空间复杂度 \(O(1)\) 代码 #include <bits/stdc++.h& ...
- c++题目:切香肠
c++题目:切香肠 题目 题目描述 有 n 条香肠,每条香肠的长度相等.我们打算将这些香肠切开后全部分给 k 名客人,且要求每名客人获得一样多的香肠.请问最少需要切几刀?注意一刀只能切断一条香肠,每个 ...
- Java lambda表达式基本使用
代码示例:java.lambda.LambdaExpression 1 本质 lambda表达式本质上是对匿名内部类实例的一种简化写法. 1.1 案例 有以下List<Integer>对象 ...
- 树莓派编译opencv4
前言 我用的是 树莓派3b 编译的 opencv4.1.0,如果不想编译可以直接下载我编译好的. 下载地址 直接 make install,或者按照我后续步骤复制动态链接库. 准备 需要调节虚拟内存大 ...
- 【SQL】DML语句-SQL更新与删除:单表更新删除与连表更新删除语法
〇.概述 1.资料 sql连表删除:http://www.lanxinbase.com/?tag=sql%E8%BF%9E%E8%A1%A8%E5%88%A0%E9%99%A4 sql实现两表关联更新 ...
- 2022NewStarCTF新生赛一些比较有意思的题目wp
Misc_蚁剑流量分析 Pcap的文件可以直接使用工具 编辑器打开目录,一个一个看,可以找到eval危险函数 看到n3wst4r,直接使用linux正则匹配,找出相关内容 Url解码,了解一下蚁剑流量 ...
- 12、HSSFWorkbook实现多张sheet导出
转载自 一.封装一个通用的装载数据的实体类: import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsCon ...
- M.2 SSD固态硬盘上安装windows问题
近来M2硬盘大降价,笔记就趁便宜买了一个2T的M.2固态硬盘,插在笔记本上,接下来安装win11,本想以前安装多次,也是老手了,没想到遇到很多问题,一度陷入僵局,不过最终还是安装成功了,下面记录下安装 ...