工具类-高效集合差异计算工具DiffWrapper

集合差异工具类-DiffWrapper

原因

在编辑过程中,肯定会存在对于子表的更新操作,这种更新分为三种: 要加的, 要删的,要更新的,并且传参只有一个modifyVO的, 每一个都写有点过于冗余,故考虑提取一个工具类

流程图思路

在这里插入图片描述
流程图对应代码如下:

graph TD
A[输入] --> B[空集合处理]
B --> C[提取旧数据ID集合]
B --> D[提取新数据ID集合]
C --> E[计算删除ID集合]
D --> F[识别新增项]
D --> G[识别修改项]
E --> H[构建结果]
F --> H
G --> H

代码如下:

@Setter
@Getter
@Slf4j
public class DiffWrapper<T, ID> {@ApiModelProperty("要添加的集合列表")private List<T> toAddList;@ApiModelProperty("要删除的ID集合列表")private List<ID> toRemoveIdList;@ApiModelProperty("要修改的列表")private List<T> toModifyList;/*** 创建差异计算结果* * @param dbItemList     数据库老数据* @param newList        前端入参新数据* @param idExtractor    ID提取函数,基于那个字段进行判断* @param fieldSelectors 可变参数指定需要比较的字段 , equal比较走的是 Objects.equals* @return 差异包装结果*/@SafeVarargspublic static <T, ID> DiffWrapper<T, ID> newInstance(List<T> dbItemList, List<T> newList, Function<T, ID> idExtractor, Function<T, ?>... fieldSelectors) {// 空集合防御List<T> safeOlds = Optional.ofNullable(dbItemList).orElse(Collections.emptyList());List<T> safeNews = Optional.ofNullable(newList).orElse(Collections.emptyList());// 提取ID集合Set<ID> oldIds = safeOlds.stream().map(idExtractor).filter(Objects::nonNull).collect(Collectors.toSet());Set<ID> newIds = safeNews.stream().map(idExtractor).filter(Objects::nonNull).collect(Collectors.toSet());// 过滤删除ID集合List<ID> removeIdList = oldIds.stream().filter(id -> !newIds.contains(id)).collect(Collectors.toList());// 新增List<T> addList = safeNews.stream().filter(item -> idExtractor.apply(item) == null).collect(Collectors.toList());// 构建旧数据映射Map<ID, T> oldMap = safeOlds.stream().collect(Collectors.toMap(idExtractor, Function.identity()));// 修改项(比较指定字段)List<T> modifyList = safeNews.stream().filter(item -> {ID id = idExtractor.apply(item);if (id == null || !oldMap.containsKey(id)) {return false;}T oldItem = oldMap.get(id);return hasFieldsChanged(oldItem, item, fieldSelectors);}).collect(Collectors.toList());return new DiffWrapper<>(addList, removeIdList, modifyList);}public static void main(String[] args) {// 测试数据准备class TestItem {Long id;String name;TestItem(Long id, String name) {this.id = id;this.name = name;}Long getId() {return id;}String getName() {return name;}@Overridepublic String toString() {return "TestItem[id=" + id + ",name=" + name + "]";}}// 场景1: 正常增删改List<TestItem> oldList1 = Arrays.asList(new TestItem(1L, "old1"), new TestItem(2L, "old2"), new TestItem(3L, "old3"));List<TestItem> newList1 = Arrays.asList(new TestItem(null, "new1"), // 新增new TestItem(2L, "updated2"), // 修改new TestItem(3L, "old3") // 未修改(内容相同));log.info("==== Scenario 1: Normal additions, deletions, and modifications ====");DiffWrapper<TestItem, Long> result1 = DiffWrapper.newInstance(oldList1, newList1, TestItem::getId);log.info("to_add_list: {}", result1.getToAddList()); // 应包含 [new1]log.info("to_remove_id_list: {}: ", result1.getToRemoveIdList()); // 应包含 [1]log.info("to_modify_list: {} ", result1.getToModifyList()); // 应包含 [updated2]// 场景2: 空集合测试log.info("\n==== Scenario 2: Empty collection test ====");DiffWrapper<TestItem, Long> result2 = DiffWrapper.newInstance(null, Collections.emptyList(), TestItem::getId);log.info("empty old result {} ", result2.getToAddList() + "/" + result2.getToRemoveIdList() + "/"+ result2.getToModifyList());// 场景3: 纯新增场景List<TestItem> newList3 = Arrays.asList(new TestItem(null, "new1"), new TestItem(null, "new2"));log.info("\n==== Scenario 3: Pure Addition ====");DiffWrapper<TestItem, Long> result3 = DiffWrapper.newInstance(Collections.emptyList(), newList3, TestItem::getId);log.info("to_add_list {} ", result3.getToAddList().size()); // 应为2// 场景4: 纯删除场景log.info("\n==== Scenario 4: Pure Delete ====");DiffWrapper<TestItem, Long> result4 = DiffWrapper.newInstance(oldList1, Collections.emptyList(), TestItem::getId);log.info("to_remove_id_list: {}", result4.getToRemoveIdList()); // 应包含1,2,3// 场景5: ID为null的特殊情况List<TestItem> oldList5 = Arrays.asList(new TestItem(null, "shouldNotExist"), new TestItem(1L, "validItem"));List<TestItem> newList5 = Arrays.asList(new TestItem(1L, "updated"));log.info("\n==== Scenario 5: Old data contains null ID ====");DiffWrapper<TestItem, Long> result5 = DiffWrapper.newInstance(oldList5, newList5, TestItem::getId);log.info("result: {} ", result5.getToRemoveIdList()); // 只应包含1// 场景6: 内容相同不应出现在修改列表log.info("\n==== Scenario 6: Content Identification Detection ====");DiffWrapper<TestItem, Long> result6 = DiffWrapper.newInstance(oldList1, Arrays.asList(new TestItem(3L, "old3")), TestItem::getId, TestItem::getName);log.info("Modification list should be empty: " + result6.getToModifyList());}/*** 构造函数** @param toAddList* @param toRemoveIdList* @param toModifyList*/private DiffWrapper(List<T> toAddList, List<ID> toRemoveIdList, List<T> toModifyList) {this.toAddList = Collections.unmodifiableList(toAddList);this.toRemoveIdList = Collections.unmodifiableList(toRemoveIdList);this.toModifyList = Collections.unmodifiableList(toModifyList);}/*** 检查指定字段是否发生变化** @param oldItem* @param newItem* @param fieldSelectors* @param <T>* @return*/@SafeVarargsprivate static <T> boolean hasFieldsChanged(T oldItem, T newItem, Function<T, ?>... fieldSelectors) {if (fieldSelectors == null || fieldSelectors.length == 0) {// 默认比较策略return !Objects.equals(oldItem, newItem);}// 比较选中的字段for (Function<T, ?> selector : fieldSelectors) {Object oldVal = selector.apply(oldItem);Object newVal = selector.apply(newItem);if (!Objects.equals(oldVal, newVal)) {// 不相等, 需要更新return true;}}return false;}

调用demo

@Override@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)public void modifyAccountingNoteItems(Long merchantId, Long noteId, List<AccountingNoteItemDO> newItems) {// 1. 获取数据库现有项List<AccountingNoteItemDO> dbItems = this.listByMerchantIdAndNoteIds(merchantId, Lists.newArrayList(noteId));// 2. 使用工具类计算更新操作DiffWrapper<AccountingNoteItemDO, Long> diffWrapper = DiffWrapper.newInstance(dbItems, newItems, AccountingNoteItemDO::getId);// 3. 执行批量操作if (!diffWrapper.getToAddList().isEmpty()) {this.saveBatch(diffWrapper.getToAddList());}if (!diffWrapper.getToModifyList().isEmpty()) {this.updateBatchById(diffWrapper.getToModifyList());}if (!diffWrapper.getToRemoveIdList().isEmpty()) {this.removeLogic(new LambdaQueryWrapperX<AccountingNoteItemDO>().eq(AccountingNoteItemDO::getMerchantId, merchantId).in(AccountingNoteItemDO::getId, diffWrapper.getToRemoveIdList()));}}

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

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

相关文章

SimBA算法实现过程

文章目录添加噪声衡量扰动示例数值总结高级索引变量名代码总体代码添加噪声 操作&#xff1a;将频率扰动通过trans( )转为像素域扰动加到原始图像上&#xff08;trans返回频率域转换为像素域的结果&#xff09; expanded (images_batch[remaining_indices] # 原始图像&…

【面试题】cookie和session 的区别

文章目录一、核心定义与存储位置二、关键区别对比三、典型使用场景四、关联与依赖总结在Web开发中&#xff0c; Cookie和 Session是两种常用的状态管理机制&#xff0c;用于在无状态的HTTP协议中保存用户信息&#xff08;如登录状态、偏好设置等&#xff09;。二者的核心区别体…

【机器学习】算法调参的两种方式:网格搜索(枚举)、随机搜索

文章目录一、网格搜索&#xff1a;穷举式的最优解寻找1、数学推导过程1. 搜索空间的数学结构2. 优化问题的数学性质3. 收敛性分析4. 误差分析2、为什么网格搜索有效&#xff1f;1. 全局最优性保证2. 可重现性与稳定性3. 参数敏感性分析3、适用场景与局限性二、随机搜索&#xf…

免费PDF翻译 离线自建

https://github.com/Byaidu/PDFMathTranslate/blob/main/docs/README_zh-CN.md https://github.com/Byaidu/PDFMathTranslate/releases 方法 针对不同的使用案例&#xff0c;我们提供不同的方法来使用我们的程序&#xff1a;1. UV 安装 安装 Python (3.10 < 版本 < 3.12)…

DeepSeek智能考试系统智能体

一、deepseek-app-1.0 1、系统要求 CentOS 7.9Python 3.8Node.js 16MySQL 8.0 2、部署步骤 运行初始化脚本&#xff1a;./scripts/setup.sh初始化数据库&#xff1a;mysql -u root -p < scripts/init_db.sql启动服务&#xff1a;./scripts/start.sh 3、访问地址 前端&…

4深度学习Pytorch-神经网络--损失函数(sigmoid、Tanh、ReLU、LReLu、softmax)

目录 激活函数 1. Sigmoid 2. Tanh 函数&#xff08;双曲正切&#xff09; 3. ReLU 函数 4. Leaky ReLU (LReLU) 5. Softmax 总结对比表 损失函数选择 激活函数 激活函数是神经网络中每个神经元&#xff08;节点&#xff09;的核心组成部分。它接收上一层所有输入的加权…

探索Trae:使用Trae CN爬取 Gitbook 电子书

在以前使用过Cursor&#xff0c;但是后期由于试用资源很少&#xff0c;免费资源用完我就卸载掉啦&#xff0c;最近又需要开展相关工作&#xff0c;因此下载了最新版的Trae。Trae 2.0最近很火&#xff0c;我正好想要爬取某一个Gitbook 电子书&#xff0c;因此尝试使用Trae和Pyth…

嵌入式知识日常问题记录及用法总结(一)

文章目录摘要问题一、内核启动流程1.1 ARM内核上电复位与BootROM执行​启动代码&#xff08;Startup Code&#xff09;执行跳转到用户程序1.2 内存管理问题二、C语言基础2.1 常量指针和指针常量区别2.2.函数指针和指针函数区别2.3 关键字Volatile2.4 队列结构体数据摘要 嵌入式…

使用Navicat备份数据库MySQL、PostGreSQL等

Navicat 支持多种数据库系统&#xff0c;可通过手动或自动方式进行数据备份&#xff0c;整个过程还是相对简单且直观&#xff0c;比自己敲命令行方便多了。一、备份步骤1.1、手动备份1、打开Navicat并连接数据库&#xff1a;首先&#xff0c;启动Navicat并连接到您的MySQL数据库…

Web3: 用ERC-1400革新公司股权激励

大家好&#xff01;今天&#xff0c;我们来聊一个非常酷的话PEG话题&#xff1a;如何利用Web3技术&#xff0c;特别是ERC-1400证券型代币标准&#xff0c;来革新传统的公司股权激励模式。 大家是否想过&#xff0c;派发给员工的期权或限制性股票&#xff08;RSU&#xff09;可以…

【Python 高频 API 速学 ④】

一、为什么是这 4 个&#xff1f; 列表&#xff08;list&#xff09;是 Python 最常用的可变序列&#xff0c; 90 % 的操作可以浓缩成 「增、并、删、排」 四个字&#xff0c; 而这四个字正好对应 append / extend / pop / sort。二、四剑客一览方法作用原地&#xff1f;返回值…

K8S的POD数量限制

#k8s-v1.11.11.查询当前节点的最大pod数kubectl describe nodes | grep pods2.编辑配置文件把单台的pod数量调到150个vim /etc/systemd/system/kubelet.service #添加最大数量--maxPods: 1503.加载配置重启服务systemctl daemon-reload systemctl restart kubelet#k8s-v1.21.41…

OpenAI开源大模型 GPT-OSS 开放权重语言模型解析:技术特性、部署应用及产业影响

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》&#xff08;跟我一起学人工智能&#xff09;【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷…

CSS--后端也有自己的CSS要学

CSS,即Cascading Style Sheets,它描述了网页的表现与展示效果 为了演示CSS,我写了一个简单的index.html 为了使用控制变量法,一开始我先不写style.css文件的内容 右键在默认浏览器里查看页面,看看效果 1-选择器 根据标签名进行匹配,所以也叫元素选择器 页面效果: 根据…

Docker swarm 常用的命令集合

#docker swarm## 初始化单节点Swarm docker swarm init# 部署测试服务 docker service create --name web --publish 8080:80 --replicas 3 nginx:alpine# Manager节点初始化&#xff08;指定IP&#xff09; docker swarm init --advertise-addr 192.168.1.100# 获取加入令牌 M…

231. 2 的幂

Problem: 231. 2 的幂 文章目录思路解题过程复杂度Code思路 2的幂 n 的二进制只有一个1&#xff0c;而 n - 1的二进制则是把 n 的二进制1变0, 0变1。 例&#xff1a;2^24100,34-1011. 解题过程 n & n - 1 0 复杂度 时间复杂度: O(1)O(1)O(1)空间复杂度: O(1)O(1)O(1) Co…

浅尝AI辅助C转Verilog方法

一、常规算法模块的开发流程日常芯片开发工作中&#xff0c;挺多看工作是把C语言转verilog。例如ISP的代码&#xff0c;都很先由算法进行C model的开发&#xff0c;验证完性能后&#xff0c;输出算法原理文档和c代码&#xff1b;数字设计接手&#xff0c;把C语言转换为verilog代…

Redis分布式锁详解:原理、实现与实战案例

目录 1. 什么是分布式锁&#xff1f; 分布式锁的核心要求 2. 基于Redis的分布式锁实现方案 &#xff08;1&#xff09;基础方案&#xff1a;SETNX EXPIRE &#xff08;2&#xff09;优化方案&#xff1a;SET NX PX&#xff08;原子性加锁&#xff09; &#xff08;3&…

【能碳建设1】用AI+开源打造物联网+能碳管理+交易SaaS系统的最短路径实施指南

摘要 本指南为技术小白设计,目标是在最短时间内利用AI工具与开源系统,独立完成一套物联网平台 + 能碳管理平台 + 碳交易系统的SaaS最小可用版本(MVP),并可后续扩展。流程分为目标定义、技术选型、环境搭建、核心功能开发、SaaS化、多租户、上线运维等环节,按天推进,每步…

CVPR中深度学习新范式:通用性、鲁棒性与多模态的创新突破

来gongzhonghao【图灵学术计算机论文辅导】&#xff0c;快速拿捏更多计算机SCI/CCF发文资讯&#xff5e;分享一个深度学习领域正在迅速升温的前沿方向&#xff1a;通用性与鲁棒性的深度神经网络架构创新。随着大模型在视觉、文本乃至多模态任务中的广泛应用&#xff0c;体现出深…