【代码优化】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:映射到现有示例 。
mapper 接口,该接口声明任何所需的映射方法 。在编译期间,MapStruct 将生成此接口的实现 。此实现使用普通的Java方法调用来在源对象和目标对象之间进行映射 。三、MapStruct 实践3.1 基本准备
- 新增三个数据库
DO类:
@Datapublic 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;}}用户补充信息:@Datapublic class UserExtInfoDO {private String favorite;public UserExtInfoDO() {}public UserExtInfoDO(String favorite) {this.favorite = favorite;}}角色信息:@Datapublic 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类:
@Datapublic 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;}}- 添加映射接口
@Mapperpublic interface MapstructConvert {/*** 获取该类自动生成的实现类的实例*/MapstructConvert INSTANCE = Mappers.getMapper(MapstructConvert.class);}- 添加一个
interface接口,使用MapStruct的@Mapper注解修饰; - 使用
Mappers添加一个INSTANCE实例(也可以使用Spring注入,后面会扩展) 。
- 自定义转换时间格式
dateFormat = "xx" 指定映射的日期格式 。- 指定默认值
defaultValue = "https://tazarkount.com/read/-"- 忽略不映射的字段
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 = "https://tazarkount.com/read/-"),// 嵌套映射@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 = "https://tazarkount.com/read/-"),@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 = "https://tazarkount.com/read/-"),// 嵌套映射@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使用合理的默认值,但在配置或实现特殊行为时技术交流,欢迎扫一扫!风尘博客

文章插图
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
