【java-Neo4j 5进阶篇】- 1.批量新增数据

news/2024/11/30 20:47:19/

系列文章目录

之前的系列文章:
一、概述篇:https://blog.csdn.net/qq_40570699/article/details/143024984
二、入门篇:https://blog.csdn.net/qq_40570699/article/details/143905723
三、进阶篇:

  • 1.批量导入数据

文章目录

  • 系列文章目录
  • 需求场景
  • 一、解决思路
  • 二、代码
    • 1.将属性更新的逻辑封装为一个通用的工具类,可以处理任意类型的对象。
    • 2.在服务端使用上述工具方法封装节点更新逻辑,无需手动处理反射或循环。
    • 3.进一步抽象为通用接口
    • 4.服务扩展
    • 5.代码调用
  • 三、总结


需求场景

代码版本及依赖在该系列入门篇已阐明

1.我需要使用Java向Neo4j新增一批数据。若数据已存在则更新非空属性的值,若不存在则新增节点数据。
2.我的节点实体很多,我想要个能够高复用的抽象代码。


一、解决思路

基于我们对ES的更新思路一致,Neo4j提供的saveAll也需要我们先查询后更新。所以我们先将我们插入的数据分类为已存在数据不存在数据对应的修改新增

二、代码

1.将属性更新的逻辑封装为一个通用的工具类,可以处理任意类型的对象。

代码如下(示例):

java">import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Objects;public class ObjectUtils {/*** 更新目标对象的非空属性* * @param source 源对象,包含最新属性值* @param target 目标对象,将被更新*/public static <T> void updateNonNullProperties(T source, T target) {if (source == null || target == null) {throw new IllegalArgumentException("Source and target objects must not be null");}Field[] fields = source.getClass().getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);Object value = field.get(source);if (value != null) {field.set(target, value);}} catch (IllegalAccessException e) {throw new RuntimeException("Error updating properties", e);}}}/*** 将一批实体更新到数据库(适用于批量更新场景)** @param sources 待更新的源对象集合* @param existingEntities 已存在的实体集合* @param getIdentifierFunction 用于提取唯一标识符的方法引用* @param saveFunction 保存方法引用* @param <T> 实体类型* @param <ID> 唯一标识符类型*/public static <T, ID> void saveOrUpdateEntities(Collection<T> sources,Collection<T> existingEntities,java.util.function.Function<T, ID> getIdentifierFunction,java.util.function.Consumer<Collection<T>> saveFunction) {// 构建已存在实体的 Map<Identifier, Entity>var existingEntityMap = existingEntities.stream().collect(java.util.stream.Collectors.toMap(getIdentifierFunction, e -> e));// 遍历源数据,更新或新增sources.forEach(source -> {ID identifier = getIdentifierFunction.apply(source);T existingEntity = existingEntityMap.get(identifier);if (existingEntity != null) {updateNonNullProperties(source, existingEntity);} else {existingEntities.add(source); // 新增}});// 保存所有数据saveFunction.accept(existingEntities);}
}

2.在服务端使用上述工具方法封装节点更新逻辑,无需手动处理反射或循环。

java">import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;@Service
public class YourNodeService {private final YourNodeRepository yourNodeRepository;public YourNodeService(YourNodeRepository yourNodeRepository) {this.yourNodeRepository = yourNodeRepository;}/*** 插入或更新节点* * @param nodes 节点列表*/@Transactionalpublic void saveOrUpdateNodes(List<YourNode> nodes) {// 提取所有 name 属性List<String> names = nodes.stream().map(YourNode::getName).toList();// 查询数据库中的已存在节点List<YourNode> existingNodes = yourNodeRepository.findByNameIn(names);// 使用通用工具方法处理更新逻辑ObjectUtils.saveOrUpdateEntities(nodes,existingNodes,YourNode::getName,yourNodeRepository::saveAll);}
}

3.进一步抽象为通用接口

java">import java.util.List;public interface GenericService<T, ID> {void saveOrUpdate(List<T> entities, java.util.function.Function<T, ID> identifierFunction);
}
java">import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Transactional;import java.util.List;
import java.util.stream.Collectors;public class GenericServiceImpl<T, ID> implements GenericService<T, ID> {private final CrudRepository<T, ID> repository;public GenericServiceImpl(CrudRepository<T, ID> repository) {this.repository = repository;}@Override@Transactionalpublic void saveOrUpdate(List<T> entities, java.util.function.Function<T, ID> identifierFunction) {// 提取所有标识符List<ID> identifiers = entities.stream().map(identifierFunction).collect(Collectors.toList());// 查询已存在的实体List<T> existingEntities = (List<T>) repository.findAllById(identifiers);// 调用通用工具更新或插入数据ObjectUtils.saveOrUpdateEntities(entities,existingEntities,identifierFunction,repository::saveAll);}
}

4.服务扩展

java">import org.springframework.stereotype.Service;@Service
public class YourNodeService extends GenericServiceImpl<YourNode, String> {public YourNodeService(YourNodeRepository yourNodeRepository) {super(yourNodeRepository);}
}

5.代码调用

java">//name为你实体中标准@Id的唯一属性
yourNodeService.saveOrUpdate(nodes, YourNode::getName);

三、总结

  • 复用性强:
    通过通用工具类或服务接口实现,支持不同实体类型的批量更新或插入逻辑。
  • 代码简洁:
    省去每次手写循环处理逻辑,业务层代码更简明。
  • 可扩展性:
    轻松扩展到其他实体类型,只需注入对应的 Repository 和标识符方法。
  • 性能优化:
    避免重复查询数据库,支持批量查询和保存,减少数据库交互次数。

http://www.ppmy.cn/news/1551298.html

相关文章

什么是Delta Lake(数据湖框架),以及Delta Lake特性和如何使用

文章目录 Delta Lake概念1、Delta Lake特性2、Delta Lake如何使用 Delta Lake概念 了解Delta Lake之前最好先去了解一下什么是数据湖&#xff0c;以及数据湖基于Hadoop、Spark的实现: 数据湖的概念&#xff08;包含数据中台、数据湖、数据仓库、数据集市的区别&#xff09;–…

QT 实现QStackedWidget切换页面开门动画

1.实现效果 以下是一个QStackedWidget,放了两个QPushButton在上面,点击切换不同的界面。 为了方便查看动画特效,设置了每个界面的背景图片。 2.实现思路 首先截取当前界面的图片,将图片一分为二,左边渲染到一个QLabel上,右边的渲染到另一个QLabel上,然后设置QProper…

《掌握Git分布式版本控制工具:从基本概念到实战应用》

1 、目标 了解 Git 基本概念 能够概述 git 工作流程 能够使用 Git 常用命令 熟悉 Git 代码托管服务 能够使用 idea 操作 git 2 、概述 2.1 、开发中的实际场景 2.2 、版本控制器的方式 2.3 、 SVN 场景一&#xff1a;备份 小明负责的模块就要完成了&#xff0c;就在…

JavaScript 入门教学:从基础语法到实践案例

JavaScript 是一种广泛应用于前端开发的编程语言&#xff0c;也是初学者入门编程的热门选择。本文将带你从零开始&#xff0c;了解 JavaScript 的基础语法&#xff0c;并通过一个简单案例帮助你更好地掌握。 一、JavaScript 简介 JavaScript 是一种脚本语言&#xff0c;通常用…

Git Rebase vs Merge:操作实例详解

在Git版本控制系统中&#xff0c;git rebase 和 git merge 是两种常用的命令&#xff0c;用于整合不同分支上的工作。本文将通过具体的操作实例来详细解释这两个命令的区别、使用场景&#xff0c;以及它们对历史记录的影响。 一、git merge 示例 假设我们有一个 main 分支和一…

ORACLE之DBA常用数据库查询

数据库信息 数据库概要select a.name "DB Name", e.global_name "Global Name", c.host_name "Host Name", c.instance_name "Instance Name" , DECODE(c.logins,RESTRICTED,YES,NO) "Restricted Mo…

Opencv+ROS实现颜色识别应用

目录 一、工具 二、原理 概念 本质 三、实践 添加发布话题 主要代码 四、成果 五、总结 一、工具 opencvros ubuntu18.04 摄像头 二、原理 概念 彩色图像&#xff1a;RGB&#xff08;红&#xff0c;绿&#xff0c;蓝&#xff09; HSV图像&#xff1a;H&#xff0…

BERT相关知识

1.分词方法 BPE 和 WordPiece 的区别&#xff1f; BPE 与 Wordpiece 都是首先初始化一个小词表&#xff0c;再根据一定准则将不同的子词合并。词表由小变大。BPE 与 Wordpiece 的最大区别在于&#xff0c;如何选择两个子词进行合并&#xff1a;BPE 选择频数最高的相邻子词合并&…