当前位置: 首页 > news >正文

Mapstruct的使用备忘【代替BeanUtils高效率属性拷贝】

文章目录

  • Mapstruct的使用备忘【代替BeanUtils高效率属性拷贝】
    • 1. 引入mapstruct依赖
    • 2. 数据准备
      • 2-1 准备一个子类,TestData
      • 2-2 准备两个类,SourceData,TargetData,属性完全一样
    • 3. 定义Mapper接口【注:这里的Mapper是mapstruct的,非mybatis的】
    • 4. 使用Mapstruct实现属性拷贝
      • 4-1 使用实例
      • 4-2 使用了Mapstruct,程序运行后,编译的目录会自动生成Mapper的实现类【了解即可】
    • 5 Mapstruct默认为浅拷贝,深拷贝的使用
      • 5-1 再新建一个Mapper,实现深拷贝
      • 5-2 编译目录下,自动生成Mapper的实现类如下,可以发现TestData 为new出来的
    • 6 集合的拷贝
      • 6-1 集合拷贝实例
    • 7 类型不一致
      • 7-1 类型不一致,拷贝机制如下
      • 7-2 上述问题,若想要在编译时就提示出来
      • 7-3 禁止隐式转换
    • 8 忽略指定字段

Mapstruct的使用备忘【代替BeanUtils高效率属性拷贝】

1. 引入mapstruct依赖

		<!--mapstruct核心--><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.6.0</version></dependency><!--mapstruct编译--><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.6.0</version></dependency>

2. 数据准备

2-1 准备一个子类,TestData

public class TestData {private String id;public String getId() {return id;}public void setId(String id) {this.id = id;}
}

2-2 准备两个类,SourceData,TargetData,属性完全一样

public class SourceData {private String id;private String name;private TestData data;private Long createTime;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public TestData getData() {return data;}public void setData(TestData data) {this.data = data;}public Long getCreateTime() {return createTime;}public void setCreateTime(Long createTime) {this.createTime = createTime;}
}
package cn.mediinfo.entity;public class TargetData {private String id;private String name;private TestData data;private Long createTime;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public TestData getData() {return data;}public void setData(TestData data) {this.data = data;}public Long getCreateTime() {return createTime;}public void setCreateTime(Long createTime) {this.createTime = createTime;}
}

3. 定义Mapper接口【注:这里的Mapper是mapstruct的,非mybatis的】

@Mapper
public interface BeanMapper { BeanMapper INSTANCE = Mappers.getMapper(BeanMapper.class);TargetData map(SourceData source);
}

4. 使用Mapstruct实现属性拷贝

4-1 使用实例

// 1. 准备一个SourceData类,设置好数据
SourceData source = new SourceData()
source.setId("123")
source.setName("abc")
source.setCreateTime(System.currentTimeMillis())
TestData testData = new TestData()
testData.setId("123")// 2. 使用Mapstruct实现属性拷贝 
TargetData target = BeanMapper.INSTANCE.map(source)// 3. 打印出目标类的属性值
System.out.println(target.getId() + ":" + target.getName() + ":" + target.getCreateTime())
//		这里可以看到Source和Target中的TestData属性是一样,即mapstruct默认是浅拷贝
System.out.println(source.getData() == target.getData())   // 结果为: true

4-2 使用了Mapstruct,程序运行后,编译的目录会自动生成Mapper的实现类【了解即可】

// 上述实例运行后,项目根目录下会出现编译后的文件,
//		target目录下打开BeanMapper(自己写的Mapper接口)路径,会看到编译后自动产生了实现类

在这里插入图片描述

public class BeanMapperImpl implements BeanMapper {public BeanMapperImpl() {}public TargetData map(SourceData source) {if (source == null) {return null;} else {TargetData targetData = new TargetData();targetData.setId(source.getId());targetData.setName(source.getName());targetData.setData(source.getData());targetData.setCreateTime(source.getCreateTime());return targetData;}}
}

5 Mapstruct默认为浅拷贝,深拷贝的使用

  • 查看上方编译后的源码,可以验证mapstruct默认是浅拷贝,所以上方实例打印结果为true
  • 若想实现深拷贝,在方法上添加注解@Mapping(mappingControl = DeepClone.class)即可

5-1 再新建一个Mapper,实现深拷贝

@Mapper
public interface BeanMapper2 {BeanMapper2 INSTANCE = Mappers.getMapper(BeanMapper2.class);// 使用@Mapping注解,加上mappingControl = DeepClone.class标识即可实现深拷贝@Mapping(source = "data", target = "data", mappingControl = DeepClone.class)TargetData map(SourceData source);
}
// 测试// 1. 准备一个SourceData类,设置好数据SourceData source = new SourceData();source.setId("123345");source.setName("abcderf");TestData testData1 = new TestData();testData1.setId("TTTTT");source.setData(testData1);source.setCreateTime(System.currentTimeMillis() );// 2. 使用Mapstruct实现属性拷贝  【这里BeanMapper2即实现了深拷贝】TargetData target = BeanMapper2.INSTANCE.map(source);System.out.println(target.getId() + ":" + target.getName() + ":" + target.getCreateTime());//		输出:false,  说明对象的子类为不通的实例,即实现了深拷贝System.out.println(source.getData() == target.getData());

5-2 编译目录下,自动生成Mapper的实现类如下,可以发现TestData 为new出来的

public class BeanMapper2Impl implements BeanMapper2 {public BeanMapper2Impl() {}public TargetData map(SourceData source) {if (source == null) {return null;} else {TargetData targetData = new TargetData();targetData.setData(this.testDataToTestData(source.getData()));targetData.setId(source.getId());targetData.setName(source.getName());targetData.setCreateTime(source.getCreateTime());return targetData;}}protected TestData testDataToTestData(TestData testData) {if (testData == null) {return null;} else {TestData testData1 = new TestData();testData1.setId(testData.getId());return testData1;}}
}

6 集合的拷贝

  • 在Mapper接口中,添加一个如下的方法即可;
List<TestData> map(List<TestData> source);   

6-1 集合拷贝实例

// 实例
// 1. Mapper中声明集合拷贝方法即可
@Mapper
public interface BeanMapper3 {BeanMapper3 INSTANCE = Mappers.getMapper(BeanMapper3.class);List<TestData> map(List<TestData> source);
}// 2. 测试实例
public class TestMapstructDemo{public static void main(String[] args) {TestData t1 = new TestData("1");TestData t2 = new TestData("3");TestData t3 = new TestData("4");List<TestData> sourceList = List.of(t1, t2, t3);// 实现集合的拷贝List<TestData> targetList = BeanMapper3.INSTANCE.map(sourceList);// 打印targetList,便可以看到各个元素的值了System.out.println( targetList );// 		这里输出:  true   说明集合中的每个TestData对象实例,  和sourceList中是一样的,即浅拷贝System.out.println( sourceList.get(0) == targetList.get(0) );}
}

7 类型不一致

7-1 类型不一致,拷贝机制如下

  • 假设将TargetData的createTime改成int类型,编译后,生成代码如下:
    在这里插入图片描述

7-2 上述问题,若想要在编译时就提示出来

可以看到它会默认帮我们转换,这是个隐藏的问题!!!
在Mapper注解上指定一些类型转换的策略,如:@Mapper(typeConversionPolicy = ReportingPolicy.ERROR)

@Mapper(typeConversionPolicy = ReportingPolicy.ERROR)

这样编译即会提示错误

java: Can

7-3 禁止隐式转换

如果将TargetData的createTime类型再改成string呢,编译又正常了,生成代码如下:
在这里插入图片描述
对于string和其它基础类型的包装类,它会隐式帮我们转换,这也是个隐藏问题,
如果希望在编译时就提示,可以自定义一个注解,并在Mapper中指定它,如下:

// 自定义注解
@Retention(RetentionPolicy.CLASS)
@MappingControl(MappingControl.Use.DIRECT)
@MappingControl(MappingControl.Use.MAPPING_METHOD)
@MappingControl(MappingControl.Use.COMPLEX_MAPPING)
public @interface ConversationMapping {
}// 使用 ==>  mappingControl = ConversationMapping.class
@Mapper(typeConversionPolicy = ReportingPolicy.ERROR, mappingControl = ConversationMapping.class)

重新编译会提示报错!!!

java: Can't map property "Long createTime" to "String createTime". Consider to declare/implement a mapping method: "String map(Long value)".

8 忽略指定字段

// 使用Mapping注解的ignore属性
@Mapping(target = "id", ignore = true)
// 如果想忽略某些字段,并且复用起来,可以定义一个IgnoreFixedField注解,然后打在方法上
@Mapping(target = "id", ignore = true)
@Mapping(target = "createTime", ignore = true)
@Mapping(target = "updateTime", ignore = true)
@Target(METHOD)
@Retention(RUNTIME)
@Documented
@interface IgnoreFixedField {
}@IgnoreFixedField   // 加上这个自定义注解后,就自动忽略id,createTime,updateTime几个属性了
@Mapping(target = "data", mappingControl = DeepClone.class)
TargetData map(SourceData source);

http://www.mrgr.cn/news/49246.html

相关文章:

  • 浅谈Java之UDP通信
  • 【解决】Win10右键“打开文件所在位置”报错“该文件没有与之关联的应用来执行该操作...”
  • 被装物联网系统|DW-S305系统是一套成熟系统
  • Cortex-M 内核的 OS 特性
  • 邮票鉴赏系统| 邮票鉴赏系统平台|基于java和vue的邮票鉴赏系统设计与实现(源码+数据库+文档)
  • 新手入门怎么炒股,新手炒股入门需要做哪些准备?
  • S4.2.6.2 LTSSM 之 Polling 状态
  • FPGA实现PCIE采集电脑端视频缩放后转千兆UDP网络输出,基于XDMA+PHY芯片架构,提供3套工程源码和技术支持
  • 宏基因组分析软件
  • 如何在数仓中处理缓慢变化维度(SCD)
  • 3.计算机网络_端口号
  • 浏览器中使用模型
  • 网络编程(19)——C++使用asio协程实现并发服务器
  • 轻松掌握TCP与UDP核心机制
  • C++初始化内置类型变量的方式(至少6种)
  • 在 Ubuntu 中使用 Gitee(码云)创建仓库、上传代码和下载
  • 进程 vs 线程:你需要知道的关键区别
  • Linux Ubuntu dbus CAPI ---- #include<dbus.h>出现“无法打开源文件dbus/xxx.h“的问题
  • 弘景光电IPO:多赛道布局展现稳健经营与可持续增长
  • github.com port 443 问题