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

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

系列文章目录

之前的系列文章:
一、概述篇: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.将属性更新的逻辑封装为一个通用的工具类,可以处理任意类型的对象。

代码如下(示例):

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.在服务端使用上述工具方法封装节点更新逻辑,无需手动处理反射或循环。

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 节点列表
     */
    @Transactional
    public 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.进一步抽象为通用接口

import java.util.List;

public interface GenericService<T, ID> {
    void saveOrUpdate(List<T> entities, java.util.function.Function<T, ID> identifierFunction);
}
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
    @Transactional
    public 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.服务扩展

import org.springframework.stereotype.Service;

@Service
public class YourNodeService extends GenericServiceImpl<YourNode, String> {

    public YourNodeService(YourNodeRepository yourNodeRepository) {
        super(yourNodeRepository);
    }
}

5.代码调用

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

三、总结

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

http://www.kler.cn/a/420497.html

相关文章:

  • ultralytics-YOLOv11的目标检测解析
  • java将word docx pdf转换为图片(不需要额外下载压缩包,直接导入maven坐标)
  • 使用Python和OpenCV自动检测并去除图像中的字幕
  • Milvus×OPPO:如何构建更懂你的大模型助手
  • 【Golang】WaitGroup 实现原理
  • Linux 系统中常用的命令
  • 1128作业
  • Debezium Engine监听binlog实现缓存更新与业务解耦
  • redhat 7.9配置阿里云yum源
  • Android 原生解析 Json 字符串
  • 《Vue零基础入门教程》第十五课:样式绑定
  • 黑马程序员MybatisPlus/Docker相关内容
  • MFC工控项目实例三十四模拟量实时监控数字显示效果
  • Git Bash + VS Code + Windows11 Git命令报错莫名奇妙的问题
  • 数据库(学习笔记)
  • YOLOv11 NCNN安卓部署
  • 【CVPR24】OmniMedVQA: 一种新的医疗LVLM大规模综合评估基准
  • 【笔记】文明、现代化与价值投资
  • 【C++boost::asio网络编程】有关异步读写api的笔记
  • 再谈Java中的String类型是否相同的判断方法
  • ESP32-S3模组上跑通ES8388(11)
  • git bash 一双击选中内容就^C (ctrl C)
  • 安全关系型数据库查询新选择:Rust 语言的 rust-query 库深度解析
  • Github提交Pull Request教程 Git基础扫盲(零基础易懂)
  • 贪心算法题
  • ipmitool使用详解(三)-解决各种dell、hp服务器无法ipmitool连接问题