Java 实体映射工具 MapStruct

简介: 让你的DO(业务实体对象),DTO(数据传输对象)数据转换更简单强大

前言

  在软件架构中,分层式结构是最常见,各层之间有其独立且隔离的业务逻辑,也因而各层有自己的输入输出对象,也就是代码中见到各种O,如DO、DTO、VO,这些数据对象之间通常都有很多相同或相近的属性对象,数据在传输的过程中从一个O到另一个O,就通常需要赋值,从最初的的get/set    


personDTO.setName(personDO.getName());
personDTO.setAge(personDO.getAge());
personDTO.setSex(personDO.getSex());
personDTO.setBirthday(personDO.getBirthday());

到后来的BeanUtils(减少了set的代码量)

image.png

再到现在的MapStruct


1.MapStruct配置

         MapStuct的使用非常简单,把对应的jar包引入即可。

<properties><lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version><org.mapstruct.version>1.3.0.Final</org.mapstruct.version><org.mapstruct.processor.version>1.3.0.Final</org.mapstruct.processor.version></properties><dependencies><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-jdk8</artifactId><version>${org.mapstruct.version}</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${org.mapstruct.processor.version}</version><scope>provided</scope></dependency></dependencies><configuration><source>${java.version}</source><target>${java.version}</target><annotationProcessorPaths><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${org.mapstruct.processor.version}</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok-mapstruct-binding</artifactId><version>${lombok-mapstruct-binding.version}</version></path></annotationProcessorPaths></configuration>


2.原理

     MapStruct属于在编译期,生成调用get/set方法进行赋值的代码,生成对应的java文件。在编译期间消耗少许的时间,换取运行时的高性能。


3.使用方法

先定义一个接口,按照规范我们在service或domainService下建一个converter包


通过依赖注入的方式获取Mapper实例

@Mapper(componentModel = "spring")




3.1 对于同名同属性的字段,无需特别声明指定,自动转换。

MapStructReq1

@Data@Accessors(chain=true)
publicclassMapStructReq1 {
privateIntegerid;
privateStringname;
@JsonFormat(pattern="yyyy-MM-dd")
privateDateupdateTime;
}

MapStructResp1:

@Data@Accessors(chain=true)
publicclassMapStructResp1 {
privateIntegerid;
privateStringname;
@JsonFormat(pattern="yyyy-MM-dd")
privateDateupdateTime;
}


converter:

image.png

@apper(componentModel

spring

publicinterfaceMaptrucconeeDemo

8

9

水*

*对于同名同属性的字段,无需特别声明指定,自动转换.

*Cparamreg

Sreturn

MapStructResp1regIToResp(MatructReqlreq;



serviceImpl:

image.png

@Service

implementsMapStructDemoService

publicclassMapStructDemoServiceImp

@Autowired

MapStructConverterDemomaptructconverterDemo

Qoverride

publicMapstructReplgToRepiMaucRqlr

returnmapstructconverterDemoITRep



controller:

image.png

**

*对于同名同属性的字段,无需特别声明指定,自动转换.

@return

GGetMapping(/demo12)

publicMapStructResplreqIToResp10

NEwMapstructReqlO.setd1.setam(nmeetUterime(eDate;

MapStructReqlreql

returnmapStrucDemoService.reaiToRespreq)

调用demo1接口后,可以看到我们给MapStructReq1赋值后,成功拷贝到了MapStructResp1中返回

image.png

fhosty/apil2/demo/mapstruct/demo1

GET

Te

Pre-requestScript

Body

Headers(8)

Params

Authorization

Body

Headers(3)

Cookies

TestResults

Pretty

Preview

Visualize

Raw

JSON

4

"traceId:"e873370e527449dbb8628c3da11b3b42"

"success":true,

"errCode":0,

"errMsg":'"success",

datas":f

"id":1,

7

8

"name":'"name",

"updateTime":"2021-02-08"

9

10

11


3.2 对于不同名相同属性的字段,可以使用Mapping注解指定。

MapStructReq1

@Data@Accessors(chain=true)
publicclassMapStructReq1 {
privateIntegerid;
privateStringname;
@JsonFormat(pattern="yyyy-MM-dd")
privateDateupdateTime;
}

MapStructResp2

@DatapublicclassMapStructResp2 {
privateIntegerid;
privateStringproductName;
@JsonFormat(pattern="yyyy-MM-dd")
privateDateupdateTime;
}

converter

@Mapping(source="name", target="productName")
MapStructResp2req1ToResp2(MapStructReq1req);


controller:

image.png

@GetMapping(/demo2)

publicMapStructResp2reqIToResp20

MaStructkqleqlneMstucRq..

mapStructDemoService.reqlToResp2(reql)

return

image.png

tthosty/apin2/demo/mapstruct/demo2

GET

Headers(8)

Body

Pre-requestScript

Params

Authorization

CookiesHeaders(3)

Body

TestResults

Pretty

Preview

Raw

JSON

Visualize

"traceId:"d888fe7a643848f5856c557c8981be8a"

"success":true,

"errCode":0,

"'errmsg":

success",

"datas":f

7

"id":1,

"productName":"name",

"updateTime":"2021-02-08"

10

11

req1中的name字段拷贝到了resp2中的productName中



3.3 支持把多个参数映射成一个类型,使用@Mapping指定即可。

converter:

@Mapping(source="req1.id", target="id")
@Mapping(source="req2.productName", target="name")
@Mapping(source="req1.updateTime", target="updateTime")
MapStructResp1req1And2ToResp1(MapStructReq1req1, MapStructReq2req2);

controller:

image.png

GGetMapping(/demo3")

publicMapStructResplreqlAnd2ToResp1O

MSTNetRnMstrucRqo

MPSTHUCRTPHSTUGRQOLAOOUTOInDTEO

etURNMAPStructDemSviceqApq

image.png

fhosty/apil2/demo/mapstruct/demo3

GET

Params

Body

Headers(8)

Pre-requestScript

Authorization

Cookies

Headers(3)

Body

TestResults

Preview

Pretty

Visualize

JSON

Raw

"traceId:"928a04ff46c84e9ad3e6686eae6bbda"

2

3

"success":true,

"errCode":0,

5

"errMsg":"'success",

6

"datas":

7

"id":1,

8

"name2",

name"

9

"updateTime":"2021-02-08"

10

t

11

将req1中的id,req2中的name拷贝到了resp1中


3.4 对于基础数据类型会进行自动隐式的转换

如int、long、String,Integer、Long等。

req3

@Data@Accessors(chain=true)
publicclassMapStructReq3 {
privateStringid;
privateintname;
@JsonFormat(pattern="yyyy-MM-dd")
privateDateupdateTime;
}

resp1:

@Data@Accessors(chain=true)
publicclassMapStructResp1 {
privateIntegerid;
privateStringname;
@JsonFormat(pattern="yyyy-MM-dd")
privateDateupdateTime;
}

converter:

image.png

**

*对于基础数据类型会进行自动隐式的转换

如intlong,StringIntegerLong等

*@paramreg

*

preturn

*/

eg3ToResp1(MapStructReq3reg

MapStructResp1

controller:

image.png

对于基础数据类型会进行自动隐式的转换

@return

*

GGetMapping(/demo5)

publicMapStructResplreqToResp1

MPSTNUCRSTESWMSTUCRESOetT)eLm2SUATeTIEEWDATEO)

returnmapStructDemoServicergToRespq)



image.png

GET

fhost/apiw2/demo/mapstruct/demo5

Params

Headers(8)

Authorization

Pre-requestScript

Body

CookiesHeaders(3)TestResults

Body

Pretty

Visualize

Raw

Preview

JSON

2

"traceId:"d6702d2od9a34a2db73a50c18abb74fc"

"success":true,

4

"errCode":0,

5

"errMsg":"'success",

"datas":1

"id":1,

8

"name":"2",

9

"updateTime":"2021-02-08"

10

11

String 类型的id转为了int型,int型的name转为了String型


3.5 集合的拷贝

req5

@Data@Accessors(chain=true)
publicclassMapStructReq5 {
privateIntegerid;
privateMapStructReq1target;
privateList<MapStructReq1>list;
}

resp5

@Data@Accessors(chain=true)
publicclassMapStructResp5 {
privateIntegerid;
privateMapStructResp1target;
privateList<MapStructResp1>list;
}

converter:

List<MapStructResp1>req1ListToResp1List(List<MapStructReq1>req1List);

controller:

image.png

属性为集合的拷贝

Greturn

QGetMapping(/demo6')

publicListMapStructRepeqLiToRepLis

L:nWMastructeql.setam

MapStructRealreg1

MEPStrUCRqLFCMSTRLO

MaStHcReqLteshoqlStukqo

MDSTHUGRL

List:MapstructReglrqListAi

etURnMaStructDeSViceqLisoRepLiLi

image.png

Headers(3)

Cookies

TestResults

Body

JSON

Pretty

Preview

Visualize

Raw

true

success

errCode":0,

5

success"

6

datas"

7

8

"id:1,

9

"name":"namel",

10

"updateTime:*2021-02-08

11

12

13

"id:2

14

"name":"name2",

"updateTime:*2021-02-08"

15

16

17

id:3,

18

19

"name":"name3",

20

"updateTime:*2021-02-08*

21

22

23

id:

24

"name":"name4",

第20210200群


3.6 嵌套对象的拷贝


converter:

MapStructResp5req5ToResp5(MapStructReq5req);


controller:

image.png

嵌套对象的拷贝

*Creturn

*/

@CetMapping(/demo7?)

publicMapStructRespreg5ToResp5

MPStHctRLet

MapStructReqlreg2neMa

WMapstructRealo.setd.setmmm

wDateQ):

MAStHuctReLe

newMapstructReqlo.setdtm

MapStructReqlreq4ne

MapStuctReg5req5-newMapstuct.eaerq)

setList(Arrays.asList(reqlqq)

returnmapStructDemoService.reg5ToResp5rea5)

target赋值为req1,给list赋值为4个不同名字的req1

image.png

CookiesHeaders(3)TestResults

ody

Status

Pretty

JSON

Visualize

Preview

Raw

errloue

"errMsg":"success"

datas*:f

1

id:5,

target"

"id:1,

10

"name":"namel",

11

"updateTime:"2021-02-08"

12

"1ist":

13

14

"id:1,

15

16

"name":"namel",

17

"updateTime:"2021-02-08"

18

19

20

"id:2,

21

"name":"name2",

22

"updateTime:2021-02-08

23

24

"iD:3,

25

26

name*:"name3",

3.7 使用java表达式进行映射

对于复杂的映射,允许使用java表达式实现字段的映射。

注意要导入使用到的类。


req6

@Data@Accessors(chain=true)
publicclassMapStructReq6 {
privateIntegerid;
privateintprice1;
privateintprice2;
}

resp6

@Data@Accessors(chain=true)
publicclassMapStructResp6 {
privateIntegerid;
privateintprice1;
privateintprice2;
}

DemoUtils

publicclassDemoUtils {
publicstaticintadd(intval1, intval2) {
returnval1+val2;
    }
}

converter:

@Mapper(componentModel="spring", imports= {DemoUtils.class})//导入java表达式使用的类,导入多个类在{}中用逗号分隔publicinterfaceMapStructConverterDemo1 {
/*** 使用java表达式进行映射* @param req* @return*/@Mapping(target="price1", expression="java(req.getPrice1() + req.getPrice2())")//直接相加@Mapping(target="price2", expression="java(DemoUtils.add(req.getPrice1(), req.getPrice2()))")//使用工具类处理MapStructResp6req6ToResp6(MapStructReq6req);
}

controller:

image.png

**

使用java表达式拷贝

areturn

*

GGetMapping(/demo8)

publicMapStructResp6reg6ToResp60

MapStructReg6reg6newMapStructReg6

setId(1)

setPrice1(10)

setPrice2(100)

eturnmapStructDemoServicereg6ToResp6reg6);

image.png

fhosty/apilya/demo/mapstruct/demo8

GET

Pre-requestScript

Body

Headers

Authorization

Params

CookiesHeaders(3)

Body

TestResults

Pretty

Visualize

Preview

Raw

JSON

"traceId:"f4e3687f845044f48a15d257b8a925b1"

3

'suCceSS":

true,

"errCode":0,

4

5

"errMsg':'success",

6

"datas":1

7

"id":1,

8

"price1":110,

9

"price2":110

10

11

参考链接

https://www.cnblogs.com/gotten/p/13052911.html

相关文章
|
27天前
|
监控 Java 测试技术
Java开发现在比较缺少什么工具?
【10月更文挑战第15天】Java开发现在比较缺少什么工具?
34 1
|
19天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
19天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
22天前
|
Web App开发 Java
使用java操作浏览器的工具selenium-java和webdriver下载地址
【10月更文挑战第12天】Selenium-java依赖包用于自动化Web测试,版本为3.141.59。ChromeDriver和EdgeDriver分别用于控制Chrome和Edge浏览器,需确保版本与浏览器匹配。示例代码展示了如何使用Selenium-java模拟登录CSDN,包括设置驱动路径、添加Cookies和获取页面源码。
|
1月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
41 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
1月前
|
Java C++
做了个Java打包工具,可以双击启动了!
本文介绍了作者日常使用Java和Swing进行开发的经验,以及Java程序分发时遇到的问题,如需要JRE环境。文中列举了几种常见的Java程序打包方法,并对比了各自的优缺点,最后作者结合这些方案,利用Winform开发了一款工具,将Java程序打包成二进制可执行文件,简化了分发流程。
做了个Java打包工具,可以双击启动了!
|
1月前
|
数据可视化 算法 Java
JAVA规则引擎工具
本文介绍了六款常用的Java规则引擎:Drools、IBM ODM、Easy Rules、jBPM、OpenL Tablets 和 Apache Camel。每款引擎都有其独特的特点和适用场景,如Drools的高效规则匹配、IBM ODM的Web界面管理、Easy Rules的轻量级特性、jBPM的流程管理、OpenL Tablets的Excel规则定义以及Apache Camel的路由和规则结合。选择合适的规则引擎可以显著提高系统的灵活性和可维护性。
|
Java 网络安全 数据安全/隐私保护
[Java工具] 邮件发送工具
注册邮箱 去163邮箱(或其他邮箱)注册一个邮箱,并开启SMTP授权码。 程序 需要注意的是,由于阿里云服务器不让使用默认的25端口,所以会出现Windows下测试发送邮件成功,Linux服务器下发送邮件却出错的问题(broke pipe、timeout、can not connect等)。
1758 0
|
8天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。