ModelMapper的常见用法 ,号称是beanUtils.copyProp....的升级版??,代码复制粘贴即可复现效果,so convenient

官网案例

以下将官网案例做一个解释

1)快速入门

递归遍历源对象的属性拷贝给目标对象

拷贝对象下对象的属性值

@Data
class Order {private Customer customer;private Address billingAddress;
}@Data
class Customer {private Name name;
}@Data
class Name {private String firstName;private String lastName;
}@Data
class Address {private String street;private String city;
}@Data
class OrderDTO {private String customerFirstName;private String customerLastName;private String billingStreet;private String billingCity;
}public class Test {public static void main(String[] args) {Order order = new Order();Customer zww = new Customer();Name name = new Name();Address address = new Address();// OrderDTO orderDTO = new OrderDTO();name.setFirstName("ww");name.setLastName("Z");zww.setName(name);address.setCity("汕头");address.setStreet("明茵路");order.setCustomer(zww);order.setBillingAddress(address);System.out.println(order);ModelMapper modelMapper = new ModelMapper();OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);System.out.println(orderDTO);}
}

image-20241011121206910

通过上述例子,我们可以将order中的customer对象中的name中的firstname拷贝到orderDto的firstname

匹配规则是根据属性名拼接,例如customerFirstname就是找customer属性对象,然后下一层的firstname,但是没有,找name的下一层,有firstname,根据属性名的映射递归一个对象的子对象,直至找到属性如果属性名

image-20241011121446292

匹配属性名从第一个字符开始匹配,如果匹配成功跳出,以至于为什么firstNameXXXdfsf能命中,但是如果是下面的情况就匹配不上

image-20241011121552370

1.1)解释

image-20241011123048411

比较源和目标类型, 根据匹配策略(后续自定义匹配器) 如果没定义策略, 会尽可能的匹配, 例如上述提到的后面加XXX和前面加XXX产生属性名不一致的匹配情况

image-20241011133308065

我们也可以设置匹配策略为松散的getConfiguration。setMatchingStrategy(常量LOOSE),这样属性名前几个字符就算不一致 也能匹配上,street。billingStreet

或者是设置一个验证, 或者添加映射规则 addmappings

2)属性映射

1.传入源值和目标值,返回一个typeMap,刚才是直接map映射且返回对象,此时我们不急着map,自定义一些规则

2.我们这里新增一个目标属性来进行测试

image-20241011141709654

image-20241011142423143

3.这里我们使用了一个addMapping添加映射规则,参数1提取源对象的属性,参数2设置属性映射给其他对象,

如果层级只有一层,可以使用方法引用,XXX::getXXX不过如果你要映射的属性在对象里的一个对象属性里面。那就不能XXX:getXXX::getAAA链式方法引用了。

参数2你也可以使用箭头函数(lambda表达式 x->x.getxxx 隐式返回x对象,类似于js)传入目标元素dest,和从第一个参数获取到的value值(默认为Object类型,且如果是字符串不能toString而是强转为String -> (String)value)。然后进行setXXX((String)value)

最后我们仅需要,调用map方法开始映射。返回值就是映射也可以说是转换后的新对象。效果如上, 官网说明如下,代码结尾再声明先展示效果,后续需要CV自己运行查看即可

但是如果你是使用的 lambda表达式,转换类型只需要

image-20241011153048954
前面统一规定即可

image-20241011143139989

4.除了这种addMapping还可以使用lambda添加多个规则/ 或者映射属性到另一个不同类型的属性上

image-20241011151154935

image-20241011151531549

5.深度映射

说白了就是第一个参数和第二个参数用lambda一个一个get进去,或者get到里面再set而已

image-20241011151615215

6.跳过映射,排除一些属性不拷贝

image-20241011151703416

规则器上调用方法传输 目标的方法引用

image-20241011152104577

3)变换器

有点类似于插件,会对映射后的值做一个统一处理,例如这里我们要把映射后的值全部变为大写

我们看一下官方文档如何解释的

image-20241011154131810

①首先用一个lambda 实现了函数式接口,什么是函数式接口?也就是一个接口里只能有一个抽象方法,和若干个静态或者默认方法,用lambda实现,函数式接口接收该实现返回的接口后续调用该接口的实现方法

image-20241011154546443

也可以实现抽象方法的基础上再调用默认方法

image-20241011154610432

如果函数式接口有多个 抽象方法,那么该接口就不能作为函数式接口

image-20241011154704162

②using使用该函数式接口

image-20241011154749693

image-20241011155016933

这个converter第一个泛型为string为src源属性值,第二个也是string,目标属性值

我们已经给这个函数式接口实现了convert方法,using即可,然后他会在映射给dest目标元素时将src做一个加工处理,调用函数式接口toUpper里的我们实现的lambda表达式方法covert传入上下文context也就是ctx然后获取源值getSource,加工后返回出来的新的src,最后映射给dest类。效果如下

image-20241011155344165

③lambda定义转换器

image-20241011155956368

官网这里没做空值判断,我们给他加一个,和前面统一一点

image-20241011160208695

看到另一个博主做了一个枚举转换

image-20241011160320615

只要搞清楚一点点原理,就比较好懂了,这里用到了lambda传入上下文,返回一个枚举类, 这个枚举类通过getInstance 传入source字符串的构造函数生成,然后映射到OrderDTO的orderSource属性上,so ez有没有,只要设置好函数式接口的入类型String和出类型 OrderSource,然后获取上下文的source对齐进行加工就可以得到美味蟹黄堡了奥里给

4)Provider设置默认值

image-20241011162723532

当映射属性的源值为null时,可以指定一个默认值,外部定义or lambda表达式

先不设置值,然后调用查看有无默认值

image-20241011163114677

5)条件判断器

image-20241011163200843

同理,外部定义还是lambda,累了不说了

image-20241011163727267

由于是空,不映射

当然也可以不用自己实现,有内置方法的提供

image-20241011165426600

官网还有具体映射的过程,这里就不看了,有兴趣的小伙伴可以自行研究,以上的用法够用了感觉

代码下面的为个人前言,有点长,不想看可以退了

代码

@Data
class Order {private Customer customer;private Address billingAddress;
}@Data
class Customer {private Name name;
}@Data
class Name {private String xxfirstName;private String lastName;
}@Data
class Address {private String street;private String city;
}@Data
class OrderDTO {private String customerFirstName;private String customerLastName;private String billingStreet;private String billingCity;private String testAddMapping;
}public class Test {public static void main(String[] args) {Order order = new Order();Customer zww = new Customer();Name name = new Name();Address address = new Address();// OrderDTO orderDTO = new OrderDTO();// name.setXxfirstName("ww");name.setLastName("Z");zww.setName(name);address.setCity("汕头");address.setStreet("明茵路");order.setCustomer(zww);order.setBillingAddress(address);System.out.println("映射前:"+order);ModelMapper modelMapper = new ModelMapper();// 创建 TypeMapTypeMap<Order, OrderDTO> typeMap = modelMapper.typeMap(Order.class, OrderDTO.class);// // 1.单个mapping规则// typeMap.addMapping(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping((String) value));// typeMap.addMapping(src->src.getCustomer().getName().getXxfirstName(),OrderDTO::setTestAddMapping);// // 2.lambda定义多个规则// typeMap.addMappings(mapper->//         mapper.<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping(value)));// typeMap.addMappings(mapper->// {//     mapper.map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping((String) value));//     mapper.map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping((String) value));//     mapper.map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping((String) value));// });// // 3.跳过映射// typeMap.addMappings(mapper->mapper.skip(OrderDTO::setCustomerLastName));// 4.变换器// Converter<String, String> toUppercase =//         ctx -> ctx.getSource() == null ? null : ctx.getSource().toUpperCase();// typeMap.addMappings(mapper->{//     mapper.using(toUppercase).<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping(value));// });// // 4.1 lambda定义变换器// typeMap.addMappings(mapper->{//     mapper.using(ctx ->(ctx.getSource()==null?null:(String)(ctx.getSource())).toUpperCase())//             .<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping(value));// });// // 5.provider默认值// typeMap.addMappings(mappper->{//     mappper.with((Provider<String> )p->{return "哈哈哈";}).<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping(value));// });// 6.条件构造器typeMap.addMappings(mapper->{mapper.when((Condition<String,String>)c->c.getSource()!=null&&!c.getSource().isEmpty()).<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest, value)->dest.setTestAddMapping(value));});// 执行映射OrderDTO map = typeMap.map(order);System.out.println("映射后:"+map);}
}

前言

在一个开源项目中看到的手法,貌似是beanutils的升级版

感觉很牛逼的一个东西,

场景举例

前端传来的对象M里有一个List和共同的name属性 根据前端的坑挖的属性名

而后端不需要这个List而是需要将A变成B,需要将List转为List

image-20241010215403120

第一个简称bm,第二个ob(object)

如何将ListVM里的 List变成List<List

自己的做法

AtomicInteger index = new AtomicInteger(1);
List<PaperQuestionTypeDto> paperQuestionTypeDto = paperDto.getPaperQuestionTypeDto();
// 这一步将前端的题型类转为后端的题型类,也就是其里面的题目类改造为order和id的
List<PaperQuestionTypeVM> paperQuestionTypeVMList = paperQuestionTypeDto.stream().map(i -> {// 后端题型类PaperQuestionTypeVM paperQuestionTypeVM = new PaperQuestionTypeVM();//改造题目类List<PaperQuestionVM> collect = i.getQuestionDtos().stream().map(p -> {PaperQuestionVM paperQuestionVM = new PaperQuestionVM();paperQuestionVM.setId(p.getId());paperQuestionVM.setItemOrder(index.getAndIncrement());return paperQuestionVM;}).collect(Collectors.toList());// 初始化name值paperQuestionTypeVM.setName(i.getName());paperQuestionTypeVM.setPaperQuestionVMS(collect);return paperQuestionTypeVM;
}).collect(Collectors.toList());

参照他人做法,先引入个依赖

<dependency><groupId>org.modelmapper</groupId><artifactId>modelmapper</artifactId><version>2.3.3</version></dependency>

配置类

package com.mindskip.xzs.utility;import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;public class ModelMapperSingle {/*** The constant modelMapper.*/protected final static ModelMapper modelMapper = new ModelMapper();private final static ModelMapperSingle modelMapperSingle = new ModelMapperSingle();static {modelMapper.getConfiguration().setFullTypeMatchingRequired(true);modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);}/*** Instance model mapper.** @return the model mapper*/public static ModelMapper Instance() {return modelMapperSingle.modelMapper;}
}

静态生成单例,加到静态方法工厂后续调用获取对象

引入并书写方法

protected final static ModelMapper modelMapper = ModelMapperSingle.Instance();AtomicInteger index = new AtomicInteger(1);// 这一步将前端的题型类转为后端的题型类,也就是其里面的题目类改造为order和id的// 方法2,使用modelMapper映射对象数据不使用set 和new直接比对相同属性名,拷贝属性和值List<PaperQuestionTypeVM> paperQuestionTypeVMList = paperQuestionTypeDto.stream().map(i -> {// 这一步将内部列表映射到另一个list,只映射属性名一致的值和属性,同步了name,省去new,setNamePaperQuestionTypeVM questionTypeVM = modelMapper.map(i, PaperQuestionTypeVM.class);List<PaperQuestionVM> questionVMS = i.getQuestionDtos().stream().map(q -> {// 将题目列表转为别的类,仅包含order和id,这里id属性一致,映射,order自己setPaperQuestionVM questionVM = modelMapper.map(q, PaperQuestionVM.class);questionVM.setItemOrder(index.getAndIncrement());return questionVM;}).collect(Collectors.toList());// 将遍历的出来新的的题目列表赋值给题型.name不用给了,映射好了questionTypeVM.setPaperQuestionVMS(questionVMS);return questionTypeVM;}).collect(Collectors.toList());

先map List将其每一个A转为B,然后再对里面的ListC遍历将C转为只有order和id的D类,返回为ListD,将ListD设置给ListB,完成了List(ListC) 为List (List)

image-20241011103125086

少了一个setId和setName,因为属性名一致,直接映射,有点类似于beanutils的copy。不过这个还可以自定义映射的属性,source的dest目的属性,做一个匹配

ModelMapper modelMapper = new ModelMapper();// 自定义映射规则
modelMapper.typeMap(UserDTO.class, User.class).addMappings(mapper -> {mapper.map(UserDTO::getUsername, User::setName); // 映射用户名mapper.map(src -> src.getAddress().getStreet(), (dest, value) -> dest.getUserAddress().setStreetName((String) value)); // 嵌套映射mapper.map(src -> src.getAddress().getCity(), (dest, value) -> dest.getUserAddress().setCityName((String) value)); // 嵌套映射
});// 执行映射
User user = modelMapper.map(userDTO, User.class);

从一个类的属性映射到另一个类的属性上

试试看用beanutils

  private static List<PaperQuestionTypeVM> getPaperQuestionTypeVMS(List<PaperQuestionTypeDto> paperQuestionTypeDto) {AtomicInteger index = new AtomicInteger(1);// 这一步将前端的题型类转为后端的题型类,也就是其里面的题目类改造为order和id的// 方法2,使用modelMapper映射对象数据不使用set 和new直接比对相同属性名,拷贝属性和值List<PaperQuestionTypeVM> paperQuestionTypeVMList = paperQuestionTypeDto.stream().map(i -> {// 这一步将内部列表映射到另一个list,只映射属性名一致的值和属性,同步了name,省去new,setName// PaperQuestionTypeVM questionTypeVM = modelMapper.map(i, PaperQuestionTypeVM.class);PaperQuestionTypeVM paperQuestionTypeVM = new PaperQuestionTypeVM();BeanUtils.copyProperties(i,paperQuestionTypeVM);List<PaperQuestionVM> questionVMS = i.getQuestionDtos().stream().map(q -> {// 将题目列表转为别的类,仅包含order和id,这里id属性一致,映射,order自己set// PaperQuestionVM questionVM = modelMapper.map(q, PaperQuestionVM.class);PaperQuestionVM paperQuestionVM = new PaperQuestionVM();BeanUtils.copyProperties(q,paperQuestionVM);paperQuestionVM.setItemOrder(index.getAndIncrement());return paperQuestionVM;}).collect(Collectors.toList());// 将遍历的出来新的的题目列表赋值给题型.name不用给了,映射好了paperQuestionTypeVM.setPaperQuestionVMS(questionVMS);return paperQuestionTypeVM;}).collect(Collectors.toList());return paperQuestionTypeVMList;}

不过这样还要多一步new出来然后再copy的操作,那个直接一步到位,copy并且new

怀着好奇心打开了官网,学习一下具体其他用法

其他用法

作用:PO(persistence object持久层也就是对应数据库列的类) 与BO(Bussiness service之间的类) 与VO(value Object 后端给前端的那玩意)和DTO(data transfer object 前端表单的字段集合那个formData) 之间的相互转换,属性拷贝

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/881544.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【笔记】自动驾驶预测与决策规划_Part5_决策过程(上)

决策过程 0. 前言1.决策过程的引入1.1有了planning&#xff0c;为什么还需要decision-making&#xff1f;1.2 决策规划的一些思考 2.马尔可夫决策过程及其关键要素2.1 马尔可夫过程2.1.1 什么是随机过程&#xff1f;2.1.2 什么是马尔科夫性&#xff1f;2.1.3 马尔可夫决策过程 …

单片机(学习)2024.10.11

目录 按键 按键原理 按键消抖 1.延时消抖 2.抬手检测 通信 1.通信是什么 2.电平信号和差分信号 3.通信的分类 (1)时钟信号划分 同步通信 异步通信 (2)通信方式划分 串行通信 并行通信 (3)通信方向划分 单工 半双工 全双工 4.USART和UART&#xff08;串口通信&a…

计算机毕业设计 基于Python的食品销售数据分析系统的设计与实现 Python毕业设计 Python毕业设计选题 数据分析 Vue【附源码+安装调试】

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

安装R和RStudio:开始你的数据分析之旅

数据分析是当今世界中一个非常热门的领域&#xff0c;而R语言是进行数据分析的强大工具之一。R是一种编程语言和软件环境&#xff0c;用于统计计算和图形表示。RStudio是一个集成开发环境&#xff08;IDE&#xff09;&#xff0c;它为R语言提供了一个更加友好和高效的工作环境。…

从commit校验失效问题探究husky原理

一、背景 之前创建的项目&#xff0c;发现代码 commit 提交的时候没有了任何校验&#xff0c;具体表现&#xff1a; 一是 feat fix 等主题格式校验没有了二是代码 lint 不通过也能提交 尝试解决这个问题&#xff0c;并深入了解husky的实现原理&#xff0c;将相关的一些知识点…

【Canvas与诗词】要做一棵树,站成永恒

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>要做一棵树站成永恒</title><style type"text/css&quo…

tauri开发Mac电脑Safari浏览器一个很奇怪的问题:在 input 输入框输入的是全小写英文字母,会自动将首字母转换为大写解决办法

问题原因 在 Mac 系统中默认使用 Safari 的内核 WKWebView 作为渲染引擎&#xff0c;而 Safari 浏览器的一些 “人性化” 机制&#xff1a;如果输入框中输入的是全小写英文&#xff0c;会自动将首字母转换为大写。 解决办法 我只需要禁止这个默认的行为&#xff0c;即可解决这…

STM32(十八):实时时钟

时间戳 Unix 时间戳&#xff08;Unix Timestamp&#xff09;定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数&#xff0c;不考虑闰秒。 时间戳存储在一个秒计数器中&#xff0c;秒计数器为32位/64位的整型变量。 世界上所有时区的秒计数器相同&#xff0c;不同时…

项目_C_Ncurses_Flappy bird小游戏

Ncurses库 概述 什么是Ncurses库&#xff1a; Ncurses是一个管理应用程序在字符终端显示的函数库&#xff0c;库中提供了创建窗口界面、移动光标、产生颜色、处理键盘按键等功能。 安装Ncurses库&#xff1a; sudo apt-get install libncurses5-dev 头文件与编译&#xf…

ECCV`24 | 新加坡国立华为提出Vista3D: 实现快速且多视角一致的3D生成

文章链接&#xff1a;https://arxiv.org/pdf/2409.12193 gitbub链接&#xff1a;https://github.com/florinshen/Vista3D 亮点直击 提出了Vista3D&#xff0c;一个用于揭示单张图像3D darkside 的框架&#xff0c;能够高效地利用2D先验生成多样的3D物体。开发了一种从高斯投影到…

初级学习:Python实现AI并搭建

随着人工智能(AI)的迅猛发展,越来越多的人希望能够学习如何通过编程实现AI应用。Python,因为其简洁易用,被广泛认为是AI开发的理想编程语言。本文将介绍Python在AI开发中的基础应用,帮助初学者入门并构建自己的AI项目。 为什么选择Python 在了解如何用Python实现AI之前,…

十、kotlin的协程

协程 基本概念定义组成挂起和恢复结构化并发协程构建器作用域构建器挂起函数阻塞与非阻塞runBlocking全局协程像守护线程 Job的生命周期 常用函数延时和等待启动和取消启动取消 暂停 协程启动调度器启动方式启动模式线程上下文继承的定义继承的公式 协程取消与超时取消挂起点取…

HTMLCSS练习

1) 效果如下 2) 代码如下 2.1) HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" conte…

Windows系统编程(三)线程并发

进程与线程 进程&#xff1a;直观的说就是任务管理器中各种正在运行的程序。对于操作系统来说&#xff0c;进程仅仅是一个数据结构&#xff0c;并不会真实的执行代码 线程&#xff1a;通常被称作但并不真的是轻量级进程或实际工作中的进程&#xff0c;它会真实的执行代码。每…

设计模式之适配器模式(Adapter)

一、适配器模式介绍 适配器模式(adapter pattern )的原始定义是&#xff1a;将类的接口转换为客户期望的另一个接口&#xff0c; 适配器可以让不兼容的两个类一起协同工作。 适配器模式是用来做适配&#xff0c;它将不兼容的接口转换为可兼容的接口&#xff0c;让原本由于接口…

2024年1月Java项目开发指南18:自定义异常输出

一般情况下&#xff0c;报错信息一大堆&#xff0c;值得注意的只有三个地方&#xff1a; 哪个文件发生了错误哪一行发生了错误错误原因是什么 只要知道这三个东西就能快速的定位到错误发生的位置并且根据提示解决。 如果你也喜欢我的这种异常输出(如文章顶部图) 那么可以参考以…

[M数学] lc3164. 优质数对的总数 II(因数分解+倍增+推公式+思维+好题)

文章目录 1. 题目来源2. 题目解析 1. 题目来源 链接&#xff1a;3164. 优质数对的总数 II 2. 题目解析 挺不错的一道 因数分解、倍增 的题目&#xff0c;需要一定的思维和推公式的能力才能解决。灵神的题解已经非常清晰易懂了&#xff0c;可以直接去看。 倍增思路&#xff…

大数据分析案例-基于逻辑回归算法构建抑郁非抑郁推文识别模型

🤵‍♂️ 个人主页:@艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏 📂加关注+ 喜欢大数据分析项目的小伙伴,希望可以多多支持该系列的其他文章 大数据分析案例合集

(十八)、登陆 k8s 的 kubernetes-dashboard 更多可视化工具

文章目录 1、回顾 k8s 的安装2、确认 k8s 运行状态3、通过 token 登陆3.1、使用现有的用户登陆3.2、新加用户登陆 4、k8s 可视化工具 1、回顾 k8s 的安装 Mac 安装k8s 2、确认 k8s 运行状态 kubectl proxy kubectl cluster-info kubectl get pods -n kubernetes-dashboard3、…

如何启动一个OpenSearch

创建两个集群&#xff0c;标注 不含备用节点 选择集群版本和配置集群版本 冷热存储和专用主节点这个按需开启 然后是网络&#xff0c;是否使用自定义域名&#xff0c;集群开在VPC还是公网上。 选择是否开启认证&#xff1a; 访问策略&#xff0c;其实就是资源策略 维护时段…