在前面, 介绍了 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); }

由于 SourceTarget 需要转化的属性是完全相同的。因此, 在 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 当然也支持多转一的操作。

AddressPerson 两个对象。

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);
}

测试

在多对一转换时, 遵循以下几个原则

  1. 当多个对象中, 有其中一个为 null, 则会直接返回 null
  2. 如一对一转换一样, 属性通过名字来自动匹配。 因此, 名称和类型相同的不需要进行特殊处理
  3. 当多个原对象中,有相同名字的属性时,需要通过 @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使用进阶(二)的更多相关文章

  1. 优雅的对象转换解决方案-MapStruct及其入门(一)

    第一次看到 MapStruct 的时候, 我个人非常的开心. 因为其跟我内心里面的想法不谋而合. 1 MapStruct 是什么? 1.1 JavaBean 的困扰 对于代码中 JavaBean之间的 ...

  2. 对象转换工具 MapStruct 介绍

    前言 在我们日常开发的分层结构的应用程序中,为了各层之间互相解耦,一般都会定义不同的对象用来在不同层之间传递数据,因此,就有了各种 XXXDTO.XXXVO.XXXBO 等基于数据库对象派生出来的对象 ...

  3. Fiddler对安卓高版本进行抓包解决方案以及分析 进阶二

    今天是2021年的最后一天了,多分享一些干货吧!看过上一章节教程后会有同学疑惑,我也一步一个脚印的,跟着流程走也设置了代理以及安装了证书,有的同学会发现 为什么手机不能够连接网络了呢?细心一点的同学会 ...

  4. 对象拷贝 - 优雅的解决方案 Mapstruct

    MapStruct GitHub 访问地址 : https://github.com/mapstruct/mapstruct/ 使用例子 : https://github.com/mapstruct/ ...

  5. mapstruct解放Java对象转换

    摘要 当前web后端开发,都是使用多层工程结构,需要在VO,BO,DTO,DO等各种数据结构中相互转换.这些转换代码都是些比较简单的字段映射,类型转换,重复性工作比较高,可以使用一些工具解放我们的双手 ...

  6. 对象转换利器之Dozer

    什么是Dozer Dozer是一个Java对象转换工具,可以在JavaBean和JavaBean之间进行递归数据复制,并且适应不同复杂的类型.Dozer会直接将名称相同的属性进行复制,属性名不同或者有 ...

  7. 手机端页面自适应解决方案—rem布局进阶版

    手机端页面自适应解决方案—rem布局进阶版   https://www.jianshu.com/p/985d26b40199 注:本文转载之处:https://www.cnblogs.com/anni ...

  8. java对象转换

    对象转换: 对象的分层涉及到各个层级之间的对象转换(Entity2DTO , DTO2VO, VO2DTO,DTO2Entity等),传统的采用set/get 方法硬编码实现写的代码比较多:或者采用B ...

  9. 我写了个IDEA开源插件,vo2dto 一键生成对象转换

    让人头疼的对象转换 头炸,po2vo.vo2do.do2dto,一堆对象属性,取出来塞进来.要不是为了 DDD 架构下的各个分层防腐,真想一竿子怼下去. 那上 BeanUtils.copyProper ...

随机推荐

  1. .NET中生成动态验证码

    .NET中生成动态验证码 验证码是图片上写上几个字,然后对这几个字做特殊处理,如扭曲.旋转.修改文字位置,然后加入一些线条,或加入一些特殊效果,使这些在人类能正常识别的同时,机器却很难识别出来,以达到 ...

  2. c++快速排序算法

    c++快速排序算法 题目描述 利用快速排序算法将读入的NN个数从小到大排序后输出. 快速排序是信息学竞赛的必备算法之一.对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成.(C++选 ...

  3. c++学习书籍推荐《C标准库(英文版)》下载

    <C标准库(英文版)>是由世界级C语言专家编写的C标准库经典著作,影响了几代程序员. <C标准库(英文版)>集中讨论了C标准库,全面介绍了ANSI/ISO C语言标准的所有库函 ...

  4. signed char类型取值范围计算

    在C语言程序中,给定一个类型,如何计算这个类型变量的取值范围呢?比如有一个字符型变量定义如下: signed char c: 这个字符变量c的取值范围是[-128,127],是计算出来的呢? 假设字符 ...

  5. ServiceFabric极简文档-5.0 Service Fabric有状态与无状态

    Service Fabric 应用程序方案 2017/08/14 作者 Edward Chen Jack Zeng Azure Service Fabric提供了一个可靠而灵活的平台,可用于编写和运行 ...

  6. ServiceFabric极简文档-1.3删除群集

    删除群集 若要删除群集,请运行包文件夹中的 RemoveServiceFabricCluster.ps1 Powershell 脚本,并传入 JSON 配置文件的路径. 可以选择性地指定删除日志的位置 ...

  7. binlog_format日志错误

    客户磁盘空间不够用,发现mysql的err日志文件已每天大概600M-800M的速度增长,开头考虑作日志切割,打开发现,整个7.8G的文件里面百分之99的文件全部是如下所示的warning警告信息 1 ...

  8. 深入理解Java虚拟机二 阅读笔记

    xl_echo编辑整理.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! --- > 以下内容摘抄自 ...

  9. 个人永久性免费-Excel催化剂功能第92波-地理地址与经纬度互转功能

    GPS设备和手机LBS的兴起,在地理信息存储过程中,在程序.应用级别是需要用经纬度去定位,而在数据分析的级别,特别是省市区镇街的分析,用到的是人可识别的文本类型存储,从设备中采集下来的数据和人工维护的 ...

  10. 个人永久性免费-Excel催化剂功能第72波-序列规则下的数据验证有效性好帮手:快速录入窗体辅助录入

    Excel作为最好用的数据录入工具,没有之一,如果能够充分利用好Excel的灵活性和规范性,将带来极大的生产力提升,前面的几波功能也有做了几大数据录入的辅助功能,今天再次给大家带来一个特定的使用场景, ...