图数据库Neo4j——SpringBoot使用Neo4j 简单增删改查 复杂查询初步

前言
图形数据库是专门用于存储图形数据的数据库,它使用图形模型来存储数据,并且支持复杂的图形查询。常见的图形数据库有Neo4j、OrientDB等。
Neo4j是用Java实现的开源NoSQL图数据库,本篇博客介绍如何在SpringBoot中使用Neo4j图数据库,如何进行简单的增删改查,以及如何进行复杂的查询。
本篇博客相关代码的git网址如下:
https://gitee.com/pet365/spring-boot-neo4j
关于Neo4j的博客文章如下:
- 图数据库Neo4j——Neo4j简介、数据结构 & Docker版本的部署安装 & Cypher语句的入门
目录
- 前言
- 引出
- springBoot整合
- 1、引入依赖
- 2、配置文件
- 3、实体类定义
- 4、dao继承Neo4jRepository
- 复杂查询
- 最短路径查询
- 最小成本查询
- 总结
引出
1.Neo4j是用Java实现的开源NoSQL图数据库;
2.SpringBoot使用Neo4j,继承Neo4jRepository进行简单增删改查;
3.使用Neo4jClient进行复杂的查询;
springBoot整合
1、引入依赖
<!-- neo4j的包--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-neo4j</artifactId></dependency>
2、配置文件
server:port: 9902
logging:level:org.springframework.data.neo4j: debug
spring:application:name: spring-neo4jdata:neo4j:database: neo4jneo4j:authentication:username: neo4jpassword: neo4j123uri: neo4j://192.168.150.101:7687
3、实体类定义

提取抽象类

不同的节点类,网点、一级转运中心、二级转运中心

4、dao继承Neo4jRepository
进行自定义查询:
| Keyword | Sample | Cypher snippet |
|---|---|---|
| After | findByLaunchDateAfter(Date date) | n.launchDate > date |
| Before | findByLaunchDateBefore(Date date) | n.launchDate < date |
| Containing (String) | findByNameContaining(String namePart) | n.name CONTAINS namePart |
| Containing (Collection) | findByEmailAddressesContains(Collection addresses) findByEmailAddressesContains(String address) | ANY(collectionFields IN [addresses] WHERE collectionFields in n.emailAddresses) ANY(collectionFields IN address WHERE collectionFields in n.emailAddresses) |
| In | findByNameIn(Iterable names) | n.name IN names |
| Between | findByScoreBetween(double min, double max) findByScoreBetween(Range range) | n.score >= min AND n.score <= max Depending on the Range definition n.score >= min AND n.score <= max or n.score > min AND n.score < max |
| StartingWith | findByNameStartingWith(String nameStart) | n.name STARTS WITH nameStart |
| EndingWith | findByNameEndingWith(String nameEnd) | n.name ENDS WITH nameEnd |
| Exists | findByNameExists() | EXISTS(n.name) |
| True | findByActivatedIsTrue() | n.activated = true |
| False | findByActivatedIsFalse() | NOT(n.activated = true) |
| Is | findByNameIs(String name) | n.name = name |
| NotNull | findByNameNotNull() | NOT(n.name IS NULL) |
| Null | findByNameNull() | n.name IS NULL |
| GreaterThan | findByScoreGreaterThan(double score) | n.score > score |
| GreaterThanEqual | findByScoreGreaterThanEqual(double score) | n.score >= score |
| LessThan | findByScoreLessThan(double score) | n.score < score |
| LessThanEqual | findByScoreLessThanEqual(double score) | n.score <= score |
| Like | findByNameLike(String name) | n.name =~ name |
| NotLike | findByNameNotLike(String name) | NOT(n.name =~ name) |
| Near | findByLocationNear(Distance distance, Point point) | distance( point(n),point({latitude:lat, longitude:lon}) ) < distance |
| Regex | findByNameRegex(String regex) | n.name =~ regex |
| And | findByNameAndDescription(String name, String description) | n.name = name AND n.description = description |
| Or | findByNameOrDescription(String name, String description) | n.name = name OR n.description = description (Cannot be used to OR nested properties) |

package com.tianju.mapper;import com.tianju.entity.AgencyEntity;
import org.mapstruct.Mapper;
import org.springframework.data.neo4j.repository.Neo4jRepository;/*** 网点的mapper,比如菜鸟驿站*/
@Mapper
public interface AgencyMapper extends Neo4jRepository<AgencyEntity,Long> {/*** 根据bid 查询* @param bid 业务id* @return 网点数据*/AgencyEntity findByBid(Long bid);/*** 根据bid删除** @param bid 业务id* @return 删除的数据条数*/Long deleteByBid(Long bid);
}

复杂查询

最短路径查询
//查询两个网点之间最短路径,查询深度最大为10
MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))WHERE n.name = "北京市昌平区定泗路" AND m.name = "上海市浦东新区南汇"RETURN path



package com.tianju.mapper.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.meta.Column;
import com.tianju.dto.OrganDTO;
import com.tianju.dto.TransportLineNodeDTO;
import com.tianju.entity.AgencyEntity;
import com.tianju.enums.OrganTypeEnum;
import com.tianju.mapper.TransportLineRepository;
import org.neo4j.driver.internal.InternalPoint2D;
import org.neo4j.driver.types.Path;
import org.neo4j.driver.types.Relationship;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.Optional;@Component
public class TransportLineRepositoryImpl implements TransportLineRepository {@Autowiredprivate Neo4jClient neo4jClient;/*** 查询最短路线* @param start 开始网点* @param end 结束网点* @return*/@Overridepublic TransportLineNodeDTO findShortestPath(AgencyEntity start, AgencyEntity end) {// 获取网点数据在Neo4j中的类型 @Node("AGENCY") @Node("OLT")String type = AgencyEntity.class.getAnnotation(Node.class).value()[0];// 构造Sql语句 $startId
// String cql = "MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))\n" +
// "WHERE n.bid = $startId AND m.bid = $endId\n" +
// "RETURN path";String cql = StrUtil.format("MATCH path = shortestPath((n:{}) -[*..10]->(m:{})) " +"WHERE n.bid = $startId AND m.bid = $endId " +"RETURN path",type,type);// 执行自定义查询Neo4jClient.RecordFetchSpec<TransportLineNodeDTO> recordFetchSpec = neo4jClient.query(cql).bind(start.getBid()).to("startId") // 替换 $startId.bind(end.getBid()).to("endId") // 替换 $endId.fetchAs(TransportLineNodeDTO.class) // 设置响应类型,指定为 TransportLineNodeDTO 类型.mappedBy((typeSystem, record) -> { // 设置结果集映射Path path = record.get(0).asPath();// 得到第一条路线TransportLineNodeDTO transportLineNodeDTO = new TransportLineNodeDTO();path.nodes().forEach(node -> { // 将每个节点信息封装成一个 OrganDto// 获得节点的 键值对 address: 上海市转运中心;bid:8002Map<String, Object> map = node.asMap();// {name=北京市昌平区定泗路,// location=Point{srid=4326, x=116.37212849638287, y=40.11765281246394},// address=北七家镇定泗路苍龙街交叉口, bid=100280, phone=010-86392987}System.out.println("map: "+map);// 把键值对转换成对象 OrganDTOOrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class);// organDTO:// OrganDTO(id=100280, name=北京市昌平区定泗路, type=null, phone=010-86392987,// address=北七家镇定泗路苍龙街交叉口, latitude=null, longitude=null)// type,latitude,longitude 没有映射成功System.out.println("organDTO: "+organDTO);// 获得标签的名称 OLT,TLT,String first = CollUtil.getFirst(node.labels());// 根据OLT获得枚举类型 OLT(1, "一级转运中心"),OrganTypeEnum organTypeEnum = OrganTypeEnum.valueOf(first);// 再获得枚举类型的 code :1、2、3organDTO.setType(organTypeEnum.getCode()); // 设置类型的映射// 经纬度 "location": point({srid:4326, x:121.59815370294322, y:31.132409729356993})InternalPoint2D location = MapUtil.get(map, "location", InternalPoint2D.class); // 经纬度 BeanUtil.getProperty(map.get("location"),"x");organDTO.setLatitude(location.x()); // 设置经纬度映射organDTO.setLongitude(location.y()); // 经纬度映射// OrganDTO(id=100280, name=北京市昌平区定泗路, type=3,// phone=010-86392987, address=北七家镇定泗路苍龙街交叉口,// latitude=116.37212849638287, longitude=40.11765281246394)System.out.println("organDTO: "+organDTO);transportLineNodeDTO.getNodeList().add(organDTO);});System.out.println("transportLineNodeDTO: "+transportLineNodeDTO);path.relationships().forEach(relationship -> {// 路径下面的关系Map<String, Object> map = relationship.asMap();Double cost = MapUtil.get(map, "cost", Double.class);transportLineNodeDTO.setCost(cost + transportLineNodeDTO.getCost());});System.out.println("transportLineNodeDTO: "+transportLineNodeDTO);return transportLineNodeDTO;});Optional<TransportLineNodeDTO> one = recordFetchSpec.one(); // Optional,1.8提供的,可以处理null的情况return one.orElse(null); // 如果为null,就返回null,如果不是null,就返回结果}
}
最小成本查询

package com.tianju.mapper.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.meta.Column;
import com.tianju.dto.OrganDTO;
import com.tianju.dto.TransportLineNodeDTO;
import com.tianju.entity.AgencyEntity;
import com.tianju.enums.OrganTypeEnum;
import com.tianju.mapper.TransportLineRepository;
import org.neo4j.driver.internal.InternalPoint2D;
import org.neo4j.driver.types.Path;
import org.neo4j.driver.types.Relationship;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.Optional;@Component
public class TransportLineRepositoryImpl implements TransportLineRepository {@Autowiredprivate Neo4jClient neo4jClient;@Overridepublic TransportLineNodeDTO findCostLeastPath(AgencyEntity start, AgencyEntity end) {String type = AgencyEntity.class.getAnnotation(Node.class).value()[0];String cqlB = "MATCH path = (n:{}) -[*..10]->(m:{}) " +"WHERE n.bid = $startId AND m.bid = $endId " +"UNWIND relationships(path) AS r " +"WITH sum(r.cost) AS cost, path " +"RETURN path ORDER BY cost ASC, LENGTH(path) ASC LIMIT 1";String cql = StrUtil.format(cqlB, type,type);Optional<TransportLineNodeDTO> one = neo4jClient.query(cql).bind(start.getBid()).to("startId").bind(end.getBid()).to("endId").fetchAs(TransportLineNodeDTO.class).mappedBy(((typeSystem, record) -> {Path path = record.get(0).asPath();TransportLineNodeDTO transportLineNodeDTO = new TransportLineNodeDTO();path.nodes().forEach(node -> {Map<String, Object> map = node.asMap();OrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class);// 获得标签的名称 OLT,TLT,String first = CollUtil.getFirst(node.labels());// 根据OLT获得枚举类型 OLT(1, "一级转运中心"),OrganTypeEnum organTypeEnum = OrganTypeEnum.valueOf(first);// 再获得枚举类型的 code :1、2、3organDTO.setType(organTypeEnum.getCode()); // 设置类型的映射// 经纬度 "location": point({srid:4326, x:121.59815370294322, y:31.132409729356993})InternalPoint2D location = MapUtil.get(map, "location", InternalPoint2D.class); // 经纬度 BeanUtil.getProperty(map.get("location"),"x");organDTO.setLatitude(location.x()); // 设置经纬度映射organDTO.setLongitude(location.y()); // 经纬度映射transportLineNodeDTO.getNodeList().add(organDTO);});path.relationships().forEach(relationship -> {// 路径下面的关系Map<String, Object> map = relationship.asMap();Double cost = MapUtil.get(map, "cost", Double.class);transportLineNodeDTO.setCost(cost + transportLineNodeDTO.getCost());});return transportLineNodeDTO;})).one();return one.orElse(null);}private void findShortestPathMy(){String cql = "MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY)) " +"WHERE n.bid = 210127 AND m.bid = 100260 " +"RETURN path";// 执行自定义查询Neo4jClient.UnboundRunnableSpec query = neo4jClient.query(cql);ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();}
}

总结
1.Neo4j是用Java实现的开源NoSQL图数据库;
2.SpringBoot使用Neo4j,继承Neo4jRepository进行简单增删改查;
3.使用Neo4jClient进行复杂的查询;
相关文章:
图数据库Neo4j——SpringBoot使用Neo4j 简单增删改查 复杂查询初步
前言 图形数据库是专门用于存储图形数据的数据库,它使用图形模型来存储数据,并且支持复杂的图形查询。常见的图形数据库有Neo4j、OrientDB等。 Neo4j是用Java实现的开源NoSQL图数据库,本篇博客介绍如何在SpringBoot中使用Neo4j图数据库&…...
-- Could NOT find livox_ros_driver (missing: livox_ros_driver_DIR)
原因 缺少livox_ros_driver 包 解决办法如下 livox_ros_driver 地址 https://github.com/Livox-SDK/livox_ros_driver 下载下来放入ros的工作目录...
山东餐饮类行业可以办理那些认证?
在招投标中很多企业会因为缺少一些证书从而丢失加分项,所以很多行业都会关注那些针对性比较强的企业认证,今天就来讲一下餐饮类行业招投标有哪些证书可以帮助企业中标? 一、ISO三体系认证 ISO9001 质量管理体系 质量是取得成功的关键。由…...
【抖音自动评论的软件】评论888无偿分享,和其开发技术与开发流程的分享
先来看成果,↑↑需要的同学可看我名字↖↖↖↖↖,或评论888无偿分享 短视频作为互联网时代的重要产物,已经成为人们生活中不可或缺的一部分。那么,如何通过短视频平台进行有效的运营和评论呢?本文将为您详细解析。 一…...
挑战100天 AI In LeetCode Day02(2)
挑战100天 AI In LeetCode Day02(2) 一、LeetCode介绍二、LeetCode 热题 HOT 100-42.1 题目2.2 题解 三、面试经典 150 题-43.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站,提供各种算法和数据结构的题目,面向程序…...
《尚医通》Vue3 项目+TypeScript 前端项目(持续更新,附带源码)
尚硅谷vue项目实战《尚医通》,Vue3项目TypeScript前端项目_哔哩哔哩_bilibili尚硅谷vue项目实战《尚医通》,Vue3项目TypeScript前端项目共计71条视频,包括:001_开篇介绍、002_尚医通项目的简介、003_Vite构建化工具初始化项目等&a…...
仪表盘 gauge
option {tooltip: {formatter: {a} <br/>{b} : {c}%},series: [{name: Pressure,type: gauge,startAngle: 225, // 起始角度,同极坐标endAngle: -45, // 终止角度,同极坐标// axisLine: {// //坐标轴轴线// show: false// },// splitLine: {//…...
常见面试题-MySQL专栏(三)MVCC、BufferPool
typora-copy-images-to: imgs 了解 MVCC 吗? 答: MVCC(Multi-Version Concurrency Control) 是用来保证 MySQL 的事务隔离性的,对一行数据的读和写两个操作默认是不会通过加锁互斥来保证隔离性,避免了频…...
CDN加速:国内外价格与企业云服务最佳搭配方案
随着互联网的快速发展,CDN(内容分发网络)已经成为了企业提供高质量、高速度内容传递的不可或缺的工具。CDN通过将内容分发到离用户更近的服务器上,提高了网站性能,减少了加载时间,改善了用户体验。在本文中…...
uni-app小程序使用vant
步骤一:安装 Vant Weapp # 通过 npm 安装 npm i vant/weapp -S --production# 通过 yarn 安装 yarn add vant/weapp --production# 安装 0.x 版本 npm i vant-weapp -S --production步骤二:在根目录下创建“wxcomponents”文件夹 步骤三:找…...
C-DS二叉树_另一棵树的子树
Description 给你两棵二叉树tree1和tree2,检验tree1中是否包含和tree2具有相同结构和结点值的子树。如果存在,输出true;否则,输出false。 Input 第一行输入t,表示有t个测试样例。 第二行首先输入n1,接着输入n1个整数,表示二叉树tree1。 第三行首先输入n2,接着输入n…...
祝贺璞华大数据产品入选中国信通院“铸基计划”
武汉璞华大数据技术有限公司HawkEye设备数字化管理平台产品,凭借优秀的产品技术能力,通过评估后,入选中国信通院“铸基计划”《高质量数字化转型产品及服务全景图(2023)》的工业数字化领域。 “铸基计划”是中国信通院推出的高质量…...
WebDAV之π-Disk派盘 + MiXplorer
MiXplorer是一款非常强大实用的手机文档管理器,能给用户提供了一系列的文档处理功能,包括本地文件浏览、文件排序、文件筛选、切换视图、新建文件、添加收藏等等,同时还能将你手机里的所有文件都罗列出来,简洁明了,让用户一眼就能够找到相应的文件并对其进行编辑,或是删除…...
java依赖的jar包下载
不需要依赖maven,直接下载jar。 仓库服务...
苹果加大对印度的扶持,提高在其生产iphone的比重
KlipC报道:跟踪苹果产业链,有分析师预计2023年全球约12%-14%的iphone在印度生产,预计2024年,印度将生产20%-25%的iphone。 KlipC的合伙人Andi D表示:“近年来随着苹果对中国的以来,印度已经成为高科技制造和…...
【漏洞复现】typecho_v1.0-14.10.10_unserialize
感谢互联网提供分享知识与智慧,在法治的社会里,请遵守有关法律法规 文章目录 漏洞利用GetShell 下载链接:https://pan.baidu.com/s/1z0w7ret-uXHMuOZpGYDVlw 提取码:lt7a 首页 漏洞点:/install.php?finish 漏洞利用 …...
Linux常见面试题
1、 冯诺依曼体系的要点是: 数字计算机的数制采用二进制,bit 位, byte 字节 1 byte 8 bit计算机应该按照程序顺序执行计算机硬件由运算器、控制器、存储器、输入设备和输出设备五大部分组成 2、计算机的硬件五个组件 控制器: 指挥系统运算器: 数学和逻…...
HarmonyOS ArkTS基础知识
概述 上一节,学习了TypeScript的基础语法,而在鸿蒙开发当中,有基于自己的编程语言,便是ArkTS。它是一种声明式UI的编程范式的语言,开发框架如下图所示: 根据框架图,分析,我将它大致…...
嵌入式课后习题第一章解答
嵌入式系统是一种以应用为中心,以计算机技术为基础,软/硬件可裁剪,适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机。它与通用PC的区别主要体现在以下几个方面: 应用针对性:嵌入式系统是专为特定应…...
postMessage
A:端口3000 import React, { useEffect } from react;function App() {useEffect(() > {const childWindow document.getElementById(child).contentWindow;const sendMessageToChild () > {childWindow.postMessage("主页面消息", "http://localhost:…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
Qt的学习(一)
1.什么是Qt Qt特指用来进行桌面应用开发(电脑上写的程序)涉及到的一套技术Qt无法开发网页前端,也不能开发移动应用。 客户端开发的重要任务:编写和用户交互的界面。一般来说和用户交互的界面,有两种典型风格&…...
基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)
注:文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件:STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...
统计学(第8版)——统计抽样学习笔记(考试用)
一、统计抽样的核心内容与问题 研究内容 从总体中科学抽取样本的方法利用样本数据推断总体特征(均值、比率、总量)控制抽样误差与非抽样误差 解决的核心问题 在成本约束下,用少量样本准确推断总体特征量化估计结果的可靠性(置…...
