图数据库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:…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
