优雅的对象转换解决方案-MapStruct使用进阶(二)
在前面, 介绍了 MapStruct
及其入门。 本文则是进一步的进阶。
在 MapStruct
生成对应的实现类的时候, 有如下的几个情景。
1 属性名称相同,则进行转化
在实现类的时候, 如果属性名称相同, 则会进行对应的转化。这个在之前的文章代码中已经有所体现。 通过此种方式, 我们可以快速的编写出转换的方法。
源对象类
import lombok.Data;
@Data
public class Source {
private String id;
private Integer num;
}
目标对象类
import lombok.Data;
@Data
public class Target {
private String id;
private Integer num;
}
转化类
@Mapper
public interface SourceMapper {
SourceMapper INSTANCE = Mappers.getMapper(SourceMapper.class);
Target source2target(Source source);
}
由于 Source
和 Target
需要转化的属性是完全相同的。因此, 在 Mapper
中, source2target
方法很快就可以编写出来了。 只需要确定入参和返回值即可。
2 属性名不相同, 可通过 @Mapping
注解进行指定转化。
属性名不相同, 在需要进行互相转化的时候, 则我们可以通过 @Mapping
注解来进行转化。
在上面的 Source
类中, 增加一个属性 totalCount
@Data
public class Source {
private String id;
private Integer num;
private Integer totalCount;
}
而对应的 Target
中, 定义的属性是 count
。
@Data
public class Target {
private String id;
private Integer num;
private Integer count;
}
如果方法没做任何的改变, 那么,在转化的时候, 由于属性名称不相同, 会导致 count 属性没有值。
这时候, 可以通过 @Mappimg
的方式进行映射。
@Mapper
public interface SourceMapper {
SourceMapper INSTANCE = Mappers.getMapper(SourceMapper.class);
@Mapping(source = "totalCount", target = "count")
Target source2target(Source source);
}
仅仅是在方法上面加了一行。再次允许测试程序。
3 Mapper 中使用自定义的转换
有时候, 对于某些类型, 无法通过代码生成器的形式来进行处理。 那么, 就需要自定义的方法来进行转换。 这时候, 我们可以在接口(同一个接口, 后续还有调用别的 Mapper
的方法)中定义默认方法(Java8及之后)。
在 Source
类中增加
private SubSource subSource;
对应的类
import lombok.Data;
@Data
public class SubSource {
private Integer deleted;
private String name;
}
相应的, 在 Target
中
private SubTarget subTarget;
对应的类
import lombok.Data;
@Data
public class SubTarget {
private Boolean result;
private String name;
}
然后在 SourceMapper
中添加方法及映射, 对应的方法更改后
@Mapper
public interface SourceMapper {
SourceMapper INSTANCE = Mappers.getMapper(SourceMapper.class);
@Mapping(source = "totalCount", target = "count")
@Mapping(source = "subSource", target = "subTarget")
Target source2target(Source source);
default SubTarget subSource2subTarget(SubSource subSource) {
if (subSource == null) {
return null;
}
SubTarget subTarget = new SubTarget();
subTarget.setResult(!subSource.getDeleted().equals(0));
subTarget.setName(subSource.getName()==null?"":subSource.getName()+subSource.getName());
return subTarget;
}
}
进行测试
4 多转一
我们在实际的业务中少不了将多个对象转换成一个的场景。 MapStruct 当然也支持多转一的操作。
有 Address
和 Person
两个对象。
import lombok.Data;
@Data
public class Address {
private String street;
private int zipCode;
private int houseNo;
private String description;
}
@Data
public class Person {
private String firstName;
private String lastName;
private int height;
private String description;
}
而在实际的使用时, 我们需要的是 DeliveryAddress
类
import lombok.Data;
@Data
public class DeliveryAddress {
private String firstName;
private String lastName;
private int height;
private String street;
private int zipCode;
private int houseNumber;
private String description;
}
其对应的信息不仅仅来自一个类, 那么, 我们也可以通过配置来实现多到一的转换。
@Mapper
public interface AddressMapper {
AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);
@Mapping(source = "person.description", target = "description")
@Mapping(source = "address.houseNo", target = "houseNumber")
DeliveryAddress personAndAddressToDeliveryAddressDto(Person person, Address address);
}
测试
在多对一转换时, 遵循以下几个原则
- 当多个对象中, 有其中一个为 null, 则会直接返回 null
- 如一对一转换一样, 属性通过名字来自动匹配。 因此, 名称和类型相同的不需要进行特殊处理
- 当多个原对象中,有相同名字的属性时,需要通过
@Mapping
注解来具体的指定, 以免出现歧义(不指定会报错)。 如上面的description
属性也可以直接从传入的参数来赋值。
@Mapping(source = "person.description", target = "description")
@Mapping(source = "hn", target = "houseNumber")
DeliveryAddress personAndAddressToDeliveryAddressDto(Person person, Integer hn);
在上面的例子中, hn
直接赋值给 houseNumber
。
5 更新 Bean 对象
有时候, 我们不是想返回一个新的 Bean 对象, 而是希望更新传入对象的一些属性。这个在实际的时候也会经常使用到。
在 AddressMapper
类中, 新增如下方法
/**
* Person->DeliveryAddress, 缺失地址信息
* @param person
* @return
*/
DeliveryAddress person2deliveryAddress(Person person);
/**
* 更新, 使用 Address 来补全 DeliveryAddress 信息。 注意注解 @MappingTarget
* @param address
* @param deliveryAddress
*/
void updateDeliveryAddressFromAddress(Address address,
@MappingTarget DeliveryAddress deliveryAddress);
注解 @MappingTarget
后面跟的对象会被更新。 以上的代码可以通过以下的测试。
@Test
public void updateDeliveryAddressFromAddress() {
Person person = new Person();
person.setFirstName("first");
person.setDescription("perSonDescription");
person.setHeight(183);
person.setLastName("homejim");
DeliveryAddress deliveryAddress = AddressMapper.INSTANCE.person2deliveryAddress(person);
assertEquals(deliveryAddress.getFirstName(), person.getFirstName());
assertNull(deliveryAddress.getStreet());
Address address = new Address();
address.setDescription("addressDescription");
address.setHouseNo(29);
address.setStreet("street");
address.setZipCode(344);
AddressMapper.INSTANCE.updateDeliveryAddressFromAddress(address, deliveryAddress);
assertNotNull(deliveryAddress.getStreet());
}
6 获取 mapper
6.1 通过 Mapper 工厂获取
在上面的例子中, 我们都是通过 Mappers.getMapper(xxx.class)
的方式来进行对应 Mapper
的获取。 此种方法为通过 Mapper 工厂获取。
如果是此种方法, 约定俗成的是在接口内定义一个接口本身的实例 INSTANCE
, 以方便获取对应的实例。
@Mapper
public interface SourceMapper {
SourceMapper INSTANCE = Mappers.getMapper(SourceMapper.class);
// ......
}
这样在调用的时候, 我们就不需要在重复的去实例化对象了。类似下面
Target target = SourceMapper.INSTANCE.source2target(source);
6.2 使用依赖注入
对于 Web 开发, 依赖注入应该很熟悉。 MapSturct
也支持使用依赖注入, 同时也推荐使用依赖注入。
值 | 注入方式 |
---|---|
default | 默认的方式, 使用 Mappers.getMapper(Class) 来进行获取 Mapper |
cdi | Contexts and Dependency Injection. 使用此种方式, 需要使用 @Inject 来进行注入 |
spring | Spring 的方式, 可以通过 @Autowired 来进行注入 |
jsr330 | 生成的 Mapper 中, 使用 @javax.inject.Named 和 @Singleton 注解, 通过 @Inject 来注入 |
6.3 依赖注入策略
可以选择是通过构造方法或者属性注入, 默认是属性注入。
public enum InjectionStrategy {
/** Annotations are written on the field **/
FIELD,
/** Annotations are written on the constructor **/
CONSTRUCTOR
}
类似如此使用
@Mapper(componentModel = "cdi", uses = EngineMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
优雅的对象转换解决方案-MapStruct使用进阶(二)的更多相关文章
- 优雅的对象转换解决方案-MapStruct及其入门(一)
第一次看到 MapStruct 的时候, 我个人非常的开心. 因为其跟我内心里面的想法不谋而合. 1 MapStruct 是什么? 1.1 JavaBean 的困扰 对于代码中 JavaBean之间的 ...
- 对象转换工具 MapStruct 介绍
前言 在我们日常开发的分层结构的应用程序中,为了各层之间互相解耦,一般都会定义不同的对象用来在不同层之间传递数据,因此,就有了各种 XXXDTO.XXXVO.XXXBO 等基于数据库对象派生出来的对象 ...
- Fiddler对安卓高版本进行抓包解决方案以及分析 进阶二
今天是2021年的最后一天了,多分享一些干货吧!看过上一章节教程后会有同学疑惑,我也一步一个脚印的,跟着流程走也设置了代理以及安装了证书,有的同学会发现 为什么手机不能够连接网络了呢?细心一点的同学会 ...
- 对象拷贝 - 优雅的解决方案 Mapstruct
MapStruct GitHub 访问地址 : https://github.com/mapstruct/mapstruct/ 使用例子 : https://github.com/mapstruct/ ...
- mapstruct解放Java对象转换
摘要 当前web后端开发,都是使用多层工程结构,需要在VO,BO,DTO,DO等各种数据结构中相互转换.这些转换代码都是些比较简单的字段映射,类型转换,重复性工作比较高,可以使用一些工具解放我们的双手 ...
- 对象转换利器之Dozer
什么是Dozer Dozer是一个Java对象转换工具,可以在JavaBean和JavaBean之间进行递归数据复制,并且适应不同复杂的类型.Dozer会直接将名称相同的属性进行复制,属性名不同或者有 ...
- 手机端页面自适应解决方案—rem布局进阶版
手机端页面自适应解决方案—rem布局进阶版 https://www.jianshu.com/p/985d26b40199 注:本文转载之处:https://www.cnblogs.com/anni ...
- java对象转换
对象转换: 对象的分层涉及到各个层级之间的对象转换(Entity2DTO , DTO2VO, VO2DTO,DTO2Entity等),传统的采用set/get 方法硬编码实现写的代码比较多:或者采用B ...
- 我写了个IDEA开源插件,vo2dto 一键生成对象转换
让人头疼的对象转换 头炸,po2vo.vo2do.do2dto,一堆对象属性,取出来塞进来.要不是为了 DDD 架构下的各个分层防腐,真想一竿子怼下去. 那上 BeanUtils.copyProper ...
随机推荐
- Java NIO学习系列三:Selector
前面的两篇文章中总结了Java NIO中的两大基础组件Buffer和Channel的相关知识点,在NIO中都是通过Channel和Buffer的协作来读写数据的,在这个基础上通过selector来协调 ...
- 模型model
django的ORM系统 ORM概念:对象关系映射(Object Relational Mapping,简称ORM)ORM的优势:不用直接编写SQL代码,只需像操作对象一样从数据库操作数据. 模型类必 ...
- iOS组件化开发一使用source管理远端库升级(四)
一.克隆远端库代码到本地选择master分支 1.克隆 2.代码会显示出你所有版本的tag 二.可以在Example目录下验证代码的正确行: cd 到库的文件夹然后 pod install comma ...
- POI 身份证号码 手机号 日期值的处理方式
private static SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ...
- Docker笔记(一):什么是Docker
原文地址: http://blog.jboost.cn/2019/07/13/docker-1.html 1. 前言 接触Docker也有两年多了,断断续续玩过一些应用场景,如安装一些常用工具服务, ...
- Promise原理探究及实现
前言 作为ES6处理异步操作的新规范,Promise一经出现就广受欢迎.面试中也是如此,当然此时对前端的要求就不仅仅局限会用这个阶段了.下面就一起看下Promise相关的内容. Promise用法及实 ...
- 简单函数编写_strcpy、_stroverchg、_strcmp
字符串复制函数 void _strcpy(char *tar, const char * res) { char *p = tar; while(assert(tar && res), ...
- Spring_简单入门(学习笔记1)
Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架. 具体介绍参考 一:IoC(Inversion of Control)控制反转,将创建对象实例反转给spri ...
- NPOI 日期类型的判断
NPOI目前我用到有两套类,一套是为了读写XLS:一套是读写XLSX 在读取文件时大都会判断单元格类型,方式大同小异,只有日期类型不同. 默认日期类型的单元格在NPOI都认为是数值类型(CellTyp ...
- 《VR入门系列教程》之16---第一个OculusVR应用
第一个VR应用 之前我们已经将Oculus的开发包导入到空工程中了,现在我们来构建第一个桌面VR的示例.开发包中已经有一个示例场景,只需要几步就可以让这个场景运行起来.我们将要构建的这个Demo ...