【代码优化】Bean映射之MapStruct

一、背景

领域模型相互转换就只能靠手工的 get()/set()

普遍的做法有以下几种:

  1. 手工 get()/set()
  2. 构造器;
  3. BeanUtils 工具类(ApacheSpring 都包含该工具类,使用方式稍稍不同);
  4. 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:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性:
    1. source:源属性;
    2. target:目标属性;
    3. dateFormat:字符串与日期之间相互转换;
    4. ignore: 某个属性不想映射,可以加上 ignore=true
    5. 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);
}
  1. 添加一个 interface 接口,使用 MapStruct@Mapper 注解修饰;
  2. 使用 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);

这样,我们就可以把 UserInfoDOUserExtInfoDO 映射为 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的更多相关文章

  1. 解析xml数据存入bean映射到数据库的 需求解决过程

    解析xml数据存入bean映射到数据库的 需求解决过程2017年12月19日 15:18:57 守望dfdfdf 阅读数:419 标签: xmlbean 更多个人分类: 工作 问题编辑版权声明:本文为 ...

  2. 5种常见Bean映射工具的性能比对

    本文由 JavaGuide 翻译自 https://www.baeldung.com/java-performance-mapping-frameworks .转载请注明原文地址以及翻译作者. 1. ...

  3. Java实体映射工具MapStruct的使用

    官网地址:http://mapstruct.org/ MapStruct 是一个代码生成器,简化了不同的 Java Bean 之间映射的处理,所谓的映射指的就是从一个实体变化成一个实体.例如我们在实际 ...

  4. 常见Bean映射工具分析评测及Orika介绍

    原地址:http://tech.dianwoda.com/2017/11/04/gao-xing-neng-te-xing-feng-fu-de-beanying-she-gong-ju-orika/ ...

  5. Bean映射工具之Apache BeanUtils VS Spring BeanUtils

    背景 在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信息,比如DTO数据传输对象和数据对象DO,我们需要将DO对象进 ...

  6. Java实体映射工具MapStruct使用详解

    1.序 通常在后端开发中经常不直接返回实体Entity类,经过处理转换返回前端,前端提交过来的对象也需要经过转换Entity实体才做存储:通常使用的BeanUtils.copyProperties方法 ...

  7. 推荐一个 Java 实体映射工具 MapStruct

    声明: 1.DO(业务实体对象),DTO(数据传输对象). 2.我的代码中用到了 Lombok ,不了解的可以自行了解一下,了解的忽略这条就好. 在一个成熟的工程中,尤其是现在的分布式系统中,应用与应 ...

  8. springboot自定义属性文件与bean映射注入属性值

    主要有几点: 一.导入依赖 springboot的包和: <dependency> <groupId>org.springframework.boot</groupId& ...

  9. 【工具库】Java实体映射工具MapStruct

    一.什么是MapStruct? MapStruct是用于代码中JavaBean对象之间的转换,例如DO转换为DTO,DTO转换为VO,或Entity转换为VO等场景,虽然Spring库和 Apache ...

随机推荐

  1. [BUUCTF]REVERSE——[V&N2020 公开赛]CSRe

    [V&N2020 公开赛]CSRe 附件 步骤: 例行检查,无壳儿,但是有NET混淆,使用de4dot工具进行处理 之后用dnSpy打开,从入口点开始看程序 找到有关flag的信息 flag由 ...

  2. C#汉字转汉语拼音

    一.使用PinYinConverterCore获取汉语拼音 最新在做一个搜索组件,需要使用汉语拼音的首字母查询出符合条件的物品名称,由于汉字存在多音字,所以自己写查询组件不太现实,因此,我们使用微软提 ...

  3. 区块链开发学习第七章:第一个Dapp-猜拳游戏

    第一个简单的Dapp-猜拳游戏.本智能合约的功能很简单,就是用户与电脑猜拳,用户选择出手后,电脑随机一个选项,然后调用智能合约方法把两个选项值传过去,在智能合约上进行比较,并通过区块链合约事件广播结果 ...

  4. CF80B Depression 题解

    Content 有一个时针,给定时间为 \(\text{HH}\) 时 \(\text{MM}\) 分,求图中 \(\alpha\) 和 \(\beta\) 角的值. 手画勿喷/kk 数据范围:\(0 ...

  5. JAVA使用netty建立websocket连接

    依赖 <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <gr ...

  6. summernote富文本图片上传,增加视频上传功能、批量上传方法

    Summernote 是一个简单灵活的所见即所得的 HTML 在线编辑器,基于 jQuery 和 Bootstrap 构建,支持快捷键操作,提供大量可定制的选项. 但是却只有图片上传功能,没有视频上传 ...

  7. RPA培训:RPA的核心三个组件常见部署方式(RPA学习天地)

    整体架构 目前主流厂商的RPA平台就是由控制台.设计器和机器人这三个标准套件组成,这三个核心套件形成了RPA产品的基本要素.其它如AI平台.人机交互.流程挖掘.自动化中心等都是衍生出来的周边产品. 1 ...

  8. 【LeetCode】252. Meeting Rooms 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 排序 日期 题目地址:https://leetcode ...

  9. 【九度OJ】题目1475:IP数据包解析 解题报告

    [九度OJ]题目1475:IP数据包解析 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1475 题目描述: 我们都学习过计算机网络, ...

  10. 面渣逆袭:JVM经典五十问,这下面试稳了!

    大家好,我是老三,"面渣逆袭"系列继续,这节我们来搞定JVM.说真的,JVM调优什么的一个程序员可能整个职业生涯都碰不到两次,但是,一旦用到的时候,那就是救命了,而且最重要的是-- ...