【代码优化】Bean映射之MapStruct
【代码优化】Bean映射之MapStruct
一、背景
领域模型相互转换就只能靠手工的
get()/set()
?
普遍的做法有以下几种:
- 手工
get()/set()
; - 构造器;
BeanUtils
工具类(Apache
和Spring
都包含该工具类,使用方式稍稍不同);Builder
模式。
这些方式都存在一些缺点:耦合性强,手工 get()/set()
经常丢参数,或者搞错参数值....
本文推荐一种效率较高的方式:MapStruct
二、理论基础
MapStruct
是一个自动生成 Bean
映射类的代码生成器,MapStruct
还能够在不同的数据类型之间进行转换。
2.1 pom.xml
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
2.2 注解关键词
@Mapper
:只有在接口加上这个注解,MapStruct
才会去实现该接口;@Mapping
:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性:source
:源属性;target
:目标属性;dateFormat
:字符串与日期之间相互转换;ignore
: 某个属性不想映射,可以加上ignore=true
;expression
:自定义指定的映射方法;
@Mappings
:配置多个@Mapping
;@MappingTarget
:映射到现有示例。
2.3 工作原理
我们要做的就是定义一个 mapper
接口,该接口声明任何所需的映射方法。在编译期间,MapStruct
将生成此接口的实现。此实现使用普通的Java
方法调用来在源对象和目标对象之间进行映射。
三、MapStruct 实践
3.1 基本准备
- 新增三个数据库
DO
类:
用户信息:
@Data
public class UserInfoDO {
private Long id;
private String userName;
private String password;
private String phoneNum;
private Date gmtBroth;
private RoleDO role;
public UserInfoDO() {
}
public UserInfoDO(RoleDO role,Long id,String userName,String password,String phoneNum,Date gmtBroth) {
this.role = role;
this.id = id;
this.userName = userName;
this.password = password;
this.phoneNum = phoneNum;
this.gmtBroth = gmtBroth;
}
}
用户补充信息:
@Data
public class UserExtInfoDO {
private String favorite;
public UserExtInfoDO() {
}
public UserExtInfoDO(String favorite) {
this.favorite = favorite;
}
}
角色信息:
@Data
public class RoleDO {
private Long id;
private String roleName;
private String description;
public RoleDO() {
}
public RoleDO(Long id, String roleName, String description) {
this.id = id;
this.roleName = roleName;
this.description = description;
}
}
- 新增一个数据传输
DTO
类:
@Data
public class UserInfoDTO {
/**
* 用户id
*/
private Long userId;
/**
* 用户名
*/
private String userName;
/**
* 用户名
*/
private String password;
/**
* 生日
*/
private String brothStr;
/**
* 手机号
*/
private String phoneNum;
/**
* 角色名
*/
private String roleName;
/**
* 喜好
*/
private String favorite;
}
- 新增一个加密工具类
public class Base64Util {
public static String encode(String str) {
BASE64Encoder encoder = new BASE64Encoder();
String encode = encoder.encode(str.getBytes());
return encode;
}
}
- 添加映射接口
@Mapper
public interface MapstructConvert {
/**
* 获取该类自动生成的实现类的实例
*/
MapstructConvert INSTANCE = Mappers.getMapper(MapstructConvert.class);
}
- 添加一个
interface
接口,使用MapStruct
的@Mapper
注解修饰; - 使用
Mappers
添加一个INSTANCE
实例(也可以使用Spring
注入,后面会扩展)。
3.2 一对一映射
- 自定义转换时间格式
通过 dateFormat = "xx"
指定映射的日期格式。
- 指定默认值
如果该值为空,则使用指定的默认值,如:defaultValue = "-"
- 忽略不映射的字段
可以通过 ignore = true
指定不需要映射的属性,如: @Mapping(target = "password", ignore = true)
。
- 嵌套映射
如果一个 DTO
中的值都是从一个对象中的多个嵌套对象映射时,如果不想一个个写映射,目标可以用 .
表示,如:
@Mapping(source = "role.roleName", target = "roleName")
- 自定义映射
当我们映射 DTO
的时候,如果某些参数的值 MapStruct
的映射配置不能满足要求,可以使用自定义方法,例如我们对手机号字段借助工具类进行加密后返回:
@Mapping(target = "phoneNum", expression = "java(cn.van.spring.copy.mapstruct.util.Base64Util.encode(userInfoDO.getPhoneNum()))")
- 完整代码如下:
@Mappings({
@Mapping(source = "id", target = "userId"),
// 自定义转换时间格式
@Mapping(source = "gmtBroth", target = "brothStr", dateFormat = "yyyy-MM-dd",defaultValue = "-"),
// 嵌套映射
@Mapping(source = "role.roleName", target = "roleName"),
// 忽略不映射的字段
@Mapping(target = "password", ignore = true),
// 自定义映射
@Mapping(target = "phoneNum", expression = "java(cn.van.spring.copy.mapstruct.util.Base64Util.encode(userInfoDO.getPhoneNum()))"),
})
UserInfoDTO doToDTO(UserInfoDO userInfoDO);
3.3 多参数映射
MapStruct
可以将几种类型的对象映射为另外一种类型,比如将多个 DO
对象转换为一个 DTO
。
@Mappings({
@Mapping(source = "userInfoDO.id", target = "userId"),
@Mapping(source = "userInfoDO.gmtBroth", target = "brothStr", dateFormat = "yyyy-MM-dd",defaultValue = "-"),
@Mapping(source = "userInfoDO.role.roleName", target = "roleName"),
// 忽略不映射的字段
@Mapping(target = "password", ignore = true),
// 自定义映射
@Mapping(target = "phoneNum", expression = "java(cn.van.spring.copy.mapstruct.util.Base64Util.encode(userInfoDO.getPhoneNum()))"),
@Mapping(source = "userExtInfoDO.favorite", target = "favorite"),
})
UserInfoDTO doToDtoMulti(UserInfoDO userInfoDO, UserExtInfoDO userExtInfoDO);
这样,我们就可以把
UserInfoDO
和UserExtInfoDO
映射为UserInfoDTO
。
3.4 集合映射
属性映射关系基于一对一的映射关系。
List<UserInfoDTO> doSToDTOS(List<UserInfoDO> userInfoDOS);
3.5 映射到现有实例
上面都是映射并生成一个新的实例,如果是想映射到已有的现有实例呢?
我们只需用 @MappingTarget
修饰。
3.6 注入 Spring
上面的示例调用时都是手动创建了一个 MapstructConvert
实例,
现在都是 Spring
的生态,MapStruct
也可以通过 Spring
注入
@Mapper(componentModel = "spring")
public interface SpringMapstructConvert {
/**
* 一对一映射
* @param userInfoDO
* @return
*/
@Mappings({
@Mapping(source = "id", target = "userId"),
// 自定义转换时间格式,如果为空,给予默认值 "-"
@Mapping(source = "gmtBroth", target = "brothStr", dateFormat = "yyyy-MM-dd",defaultValue = "-"),
// 嵌套映射
@Mapping(source = "role.roleName", target = "roleName"),
// 忽略不映射的字段
@Mapping(target = "password", ignore = true),
// 自定义映射
@Mapping(target = "phoneNum", expression = "java(cn.van.spring.copy.mapstruct.util.Base64Util.encode(userInfoDO.getPhoneNum()))"),
})
UserInfoDTO doToDTO(UserInfoDO userInfoDO);
}
相较于前者:干掉了初始化的 INSTANCE
,@Mapper
注解加入了 componentModel = "spring"
。
注意:默认是以覆盖原有值的方式映射的,如果要保留原有的值,使用
ignore
忽略字段即可。
四、总结
- 与手工编写映射代码相比
MapStruct
通过生成繁琐且易于编写的代码来节省时间。遵循约定优于配置方法,MapStruct
使用合理的默认值,但在配置或实现特殊行为时
【代码优化】Bean映射之MapStruct的更多相关文章
- 解析xml数据存入bean映射到数据库的 需求解决过程
解析xml数据存入bean映射到数据库的 需求解决过程2017年12月19日 15:18:57 守望dfdfdf 阅读数:419 标签: xmlbean 更多个人分类: 工作 问题编辑版权声明:本文为 ...
- 5种常见Bean映射工具的性能比对
本文由 JavaGuide 翻译自 https://www.baeldung.com/java-performance-mapping-frameworks .转载请注明原文地址以及翻译作者. 1. ...
- Java实体映射工具MapStruct的使用
官网地址:http://mapstruct.org/ MapStruct 是一个代码生成器,简化了不同的 Java Bean 之间映射的处理,所谓的映射指的就是从一个实体变化成一个实体.例如我们在实际 ...
- 常见Bean映射工具分析评测及Orika介绍
原地址:http://tech.dianwoda.com/2017/11/04/gao-xing-neng-te-xing-feng-fu-de-beanying-she-gong-ju-orika/ ...
- Bean映射工具之Apache BeanUtils VS Spring BeanUtils
背景 在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信息,比如DTO数据传输对象和数据对象DO,我们需要将DO对象进 ...
- Java实体映射工具MapStruct使用详解
1.序 通常在后端开发中经常不直接返回实体Entity类,经过处理转换返回前端,前端提交过来的对象也需要经过转换Entity实体才做存储:通常使用的BeanUtils.copyProperties方法 ...
- 推荐一个 Java 实体映射工具 MapStruct
声明: 1.DO(业务实体对象),DTO(数据传输对象). 2.我的代码中用到了 Lombok ,不了解的可以自行了解一下,了解的忽略这条就好. 在一个成熟的工程中,尤其是现在的分布式系统中,应用与应 ...
- springboot自定义属性文件与bean映射注入属性值
主要有几点: 一.导入依赖 springboot的包和: <dependency> <groupId>org.springframework.boot</groupId& ...
- 【工具库】Java实体映射工具MapStruct
一.什么是MapStruct? MapStruct是用于代码中JavaBean对象之间的转换,例如DO转换为DTO,DTO转换为VO,或Entity转换为VO等场景,虽然Spring库和 Apache ...
随机推荐
- Element-UI 使用 class 方式和 css 方式引入图标
今天在使用 vxe-table 时,需要引入 Element UI的图标,顺便就找了下这些组件库中图标的引用方式. 我们知道 Element .Ant Design.Font Awesome 等很多组 ...
- Visual SVN安装使用教程
visual svn使用教程 SVN简介: 为什么要使用SVN? 程序员在编写程序的过程中,每个程序员都会生成很多不同的版本,这就需要程序员有效的管理代码,在需要的时候可以迅速,准确取出相应的版本. ...
- maven打包 运行出现 错误: 找不到或无法加载主类 jar
使用maven进行打包成jar包后 使用java -jar运行jar包 出现 错误: 找不到或无法加载主类 jar 主要是由于依赖没下载好,重新下载依赖 以及要在项目的pom.xml里面添加 < ...
- SpringBoot使用 MyBatis Plus 实现物理分页查询
一.分页配置在MyBatis Plus 可以直接使用selectPage这样的分页,但返回的数据确实是分页后的数据,但在控制台打印的SQL语句其实并没有真正的物理分页,而是通过缓存来获得全部数据中再进 ...
- JS代码日期格式化
function dateConvert(format,value) { var date = new Date(value); var o = { "M+" : date.get ...
- 【九度OJ】题目1434:今年暑假不AC 解题报告
[九度OJ]题目1434:今年暑假不AC 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1434 题目描述: "今年暑假不A ...
- 【LeetCode】63. Unique Paths II 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址:https://leetcode.com/problems/unique-pa ...
- 【剑指Offer】字符串的排列 解题报告(Python)
[剑指Offer]字符串的排列 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://www.nowcoder.com/ta/coding-interviews 题 ...
- sql-labs 1-14
less-1: 1.采用二分法进行猜列: http://192.236.147.191:30000/Less-1/?id=1' order by 10--+ Welcome Dhakkan Un ...
- 如何把 MySQL 备份验证性能提升 10 倍
JuiceFS 非常适合用来做 MySQL 物理备份,具体使用参考我们的官方文档.最近有个客户在测试时反馈,备份验证的数据准备(xtrabackup --prepare)过程非常慢.我们借助 Juic ...