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

Spring Data Neo4j 学习

目录
  • 依赖
  • 配置 Neo4j
  • 创建实体类节点
  • 节点间的关系
  • 使用 Neo4jTemplate 对图数据进行 CRUD
    • 创建节点和关系
    • 查询节点
    • 更新节点信息
    • 删除节点
    • 完整
  • 使用 repository 对图数据进行 CRUD
    • 创建 Repository
    • 创建节点和关系
    • 查询
    • 更新
    • 删除
    • 完整

根据这个 https://github.com/WuYiheng-Og/neo4j_springboot 来实践

注:本文所使用SpringBoot版本为3.5.4,Java17,neo4j-5.25.1
本文将会结合官网的一个 【导演-电影-演员】关系图来进行实现。以新海诚导演的《你的名字》为例。
[Pasted image 20250803165044.png]

{"identity": 22,"labels": ["Movie"],"properties": {"tagline": "影片讲述了男女高中生在梦中相遇,并寻找彼此的故事。","title": "你的名字"},"elementId": "4:539d38bc-240d-4be5-8ab6-d144d21a7c26:22"
}{"identity": 26,"labels": ["Person"],"properties": {"born": 1997,"name": "上白石萌音"},"elementId": "4:539d38bc-240d-4be5-8ab6-d144d21a7c26:26"
}{"identity": 27,"labels": ["Person"],"properties": {"born": 1993,"name": "神木隆之介"},"elementId": "4:539d38bc-240d-4be5-8ab6-d144d21a7c26:27"
}{"identity": 28,"labels": ["Person"],"properties": {"born": 1973,"name": "新海诚"},"elementId": "4:539d38bc-240d-4be5-8ab6-d144d21a7c26:28"
}

依赖

<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <parent>        <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-parent</artifactId>  <version>3.5.4</version>  <relativePath/> <!-- lookup parent from repository -->  </parent>  <groupId>cn.tmesh</groupId>  <artifactId>neo4jdemo</artifactId>  <version>0.0.1-SNAPSHOT</version>  <name>neo4jdemo</name>  <description>neo4jdemo</description>  <properties>        <java.version>17</java.version>  </properties>  <dependencies>        <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-test</artifactId>  <scope>test</scope>  </dependency>  <dependency>            <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-data-neo4j</artifactId>  </dependency>  
<!-- neo4j 驱动 这个需要自己手动添加一下,主意,这里不指定版本,会自动选择 4x 版本 --><dependency>  <groupId>org.neo4j.driver</groupId>  <artifactId>neo4j-java-driver</artifactId>  <version>5.28.5</version>  </dependency>  <dependency>            <groupId>org.projectlombok</groupId>  <artifactId>lombok</artifactId>  <optional>true</optional>  </dependency>  </dependencies>  <build>        <plugins>  <plugin>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-maven-plugin</artifactId>  </plugin>  </plugins>  </build>  </project>

配置 Neo4j

spring:neo4j:uri: bolt://<YourNeo4jIpAddress>:7687authentication:username: <yourUserName>password: <yourPassword>
# 指定数据库data:neo4j:database: <yourDatabase>

创建 utils 包,并在该包下创建 ExampleCommandLineRunner 来装配 Driver 和 Session

package cn.tmesh.neo4jdemo.utils;  import lombok.extern.slf4j.Slf4j;  
import org.neo4j.driver.Driver;  
import org.neo4j.driver.Session;  
import org.springframework.boot.CommandLineRunner;  
import org.springframework.context.ConfigurableApplicationContext;  
import org.springframework.context.annotation.Bean;  
import org.springframework.stereotype.Component;  @Component  
@Slf4j  
public class ExampleCommandLineRunner implements CommandLineRunner {  private final Driver driver;  private final ConfigurableApplicationContext applicationContext;  public final Session session;  @Bean  Session session(){  return session;  }  // Autowire the Driver bean by constructor injection  public ExampleCommandLineRunner(Driver driver, ConfigurableApplicationContext applicationContext) {  this.driver = driver;  this.applicationContext = applicationContext;  this.session = driver.session();  }  @Override  public void run(String... args) throws Exception {  }
}

创建实体类节点

创建 entity 包,添加实体类:PersonEntityMovieEntity

package cn.tmesh.neo4jdemo.entity;  import lombok.Data;  
import org.springframework.data.neo4j.core.schema.*;  import java.util.ArrayList;  
import java.util.List;  @Node(labels = "Movie")  
@Data  
public class MovieEntity {  @Id  @GeneratedValue // Id自增  private Long id;  private final String title;  @Property("tagline") // 映射到neo4j的属性名  private final String description;  public MovieEntity(String title, String description) {  this.id = null;// 生成 node 时自动生成  this.title = title;  this.description = description;  }  // 用户指定特定的Id  public MovieEntity withId(Long id) {  if (this.id!= null && this.id.equals(id)) {  return this;  } else {  MovieEntity newObject = new MovieEntity(this.title, this.description);  newObject.id = id;  return newObject;  }  }  // 定义一个关系(参演)  @Relationship(type = "ACTED_IN", direction = Relationship.Direction.INCOMING)  private List<Roles> actorsAndRoles = new ArrayList<>();  // 定义一个关系(导演)  @Relationship(type = "DIRECTED", direction = Relationship.Direction.INCOMING)  private List<PersonEntity> directors = new ArrayList<>();  // 注意这些关系最终的箭头指向是当前实体,即TargetNode(PersonEntity)->当前定义Relationship的实体(MovieEntity)  }
package cn.tmesh.neo4jdemo.entity;  import lombok.Data;  
import org.springframework.data.neo4j.core.schema.*;  /**  * 创作一个对应 Person 实体对象 -> 对应我们 Neo4j 数据库中的 Node 对象  */  
@Node("Person")  
@Data  
public class PersonEntity {  @Id  @GeneratedValue    private Long id;  private String name;  private Integer born;  public PersonEntity(Integer born, String name) {  this.name = name;  this.born = born;  }  
}

节点间的关系

package cn.tmesh.neo4jdemo.entity;  import lombok.AllArgsConstructor;  
import lombok.Data;  
import org.springframework.data.neo4j.core.schema.RelationshipId;  
import org.springframework.data.neo4j.core.schema.RelationshipProperties;  
import org.springframework.data.neo4j.core.schema.TargetNode;  import java.util.List;  /**  * 定义一个关系属性  */  
@RelationshipProperties  
public class Roles {  @RelationshipId  private Long id;  private final List<String> roles;  @TargetNode // 相当于@StartNode  private final PersonEntity person;  // 参数1是目标关系实体节点 参数2是关系属性  // Roles 参数1:Person 实体,演员的出生年和姓名;参数2:演员名字列表(考虑到一个演员可能参演多个角色)  public Roles(PersonEntity person, List<String> roles) {  this.person = person;  this.roles = roles;  }  public List<String> getRoles() {  return roles;  }  
}

注意这些关系 @TargetNode 修饰的是关系箭头的尾部, 最终的箭头指向是当前实体,即TargetNode(PersonEntity)->当前定义 Relationship 的实体(MovieEntity)
MovieEntity 里面的这一段

// 定义一个关系(参演)@Relationship(type = "ACTED_IN", direction = Relationship.Direction.INCOMING)private List<Roles> actorsAndRoles = new ArrayList<>();// 定义一个关系(导演)@Relationship(type = "DIRECTED", direction = Relationship.Direction.INCOMING)private List<PersonEntity> directors = new ArrayList<>();// 注意这些关系最终的箭头指向是当前实体,即TargetNode(PersonEntity)->当前定义Relationship的实体(MovieEntity)}

使用 Neo4jTemplate 对图数据进行 CRUD

创建节点和关系

@Test  
void creteNodeAndRelationship() {  // 创建节点实体  MovieEntity movie = new MovieEntity("你的名字","影片讲述了男女高中生在梦中相遇,并寻找彼此的故事。");// 电影实体节点  // 定义(参演)关系  // new Roles 参数1:Person实体,演员的出生年和姓名;参数2:演员名字列表(考虑到一个演员可能参演多个角色)  // 参数1是目标关系实体节点 参数2是关系属性  Roles roles1 = new Roles(new PersonEntity(1998,"上白石萌音"), Collections.singletonList("宫水三叶"));  Roles roles2 = new Roles(new PersonEntity(1993,"神木隆之介"), Collections.singletonList("立花泷"));  PersonEntity director = new PersonEntity(1973,"新海诚");  // 添加movie的演员实体,加入(参演)关系  movie.getActorsAndRoles().add(roles1);  movie.getActorsAndRoles().add(roles2);  movie.getDirectors().add(director);  // 存入图数据库持久化  neo4jTemplate.save(movie);  
}

查询节点

@Test  
void selectNode() {  // 查询(不太推荐用Neo4jTemplate进行过滤查询,因为需要手动写cypherQuery,需要开发人员了解一下cypherQuery的写法)  Optional<PersonEntity> person;  // 1. 通过id查询  person = neo4jTemplate.findById(12, PersonEntity.class);  System.out.println("id为12号的Person节点:\n"+person);  // 2. 通过属性查询节点,如name 需要手写cypherQuery语句  Map<String,Object> map = new HashMap<>();  map.put("name","新海诚");  // 两种写法都对,看个人喜好 n是一个变量随意取,{}或者where填写query的filter过滤条件  person = neo4jTemplate.findOne("MATCH (n:Person {name: $name}) RETURN n",map, PersonEntity.class);  // person = neo4jTemplate.findOne("MATCH (n:Person) WHERE n.name = $name RETURN n",map, PersonEntity.class);  System.out.println("\n查询名字为新海诚的Person节点:\n"+person);  // 3. 通过属性关系查询节点  map = new HashMap<>();  map.put("roles",Collections.singletonList("宫水三叶"));  // 方法1.使用toExecutableQuery查询  QueryFragmentsAndParameters parameters = new QueryFragmentsAndParameters(  "MATCH (person:Person) -[ relation:ACTED_IN]-> (movie:Movie) \n" +  "WHERE relation.roles = $roles\n" +  "RETURN person",map);  List<PersonEntity> roles = neo4jTemplate.toExecutableQuery(PersonEntity.class, parameters).getResults();  // 方法2.使用 findOne 查询  // Optional<PersonEntity> roles = neo4jTemplate.findOne(  // "MATCH (person:Person) -[ relation:ACTED_IN]-> (movie:Movie) \n" +    // "WHERE relation.roles = $roles\n" +    // "RETURN person",map,PersonEntity.class);  System.out.println("\n查询角色为“宫水三叶”的演员:\n"+roles);  
}

更新节点信息

@Test  
void updateNode() {  Optional<PersonEntity> person;  Map<String,Object> map = new HashMap<>();  map.put("roles",Collections.singletonList("宫水三叶"));  // 1. 通过id查询  person = neo4jTemplate.findById(12, PersonEntity.class);  Long userId = person.get().getId();// 记录当前查询的"新海诚"的节点id  // 更新①---------更新“新海诚”的name为曾用名“新津诚”(这是他的曾用名)  map.put("name","新海诚");  map.put("usedName","新津诚");  QueryFragmentsAndParameters queryFragmentsAndParameters =  new QueryFragmentsAndParameters(  "MATCH (n:Person{name: $name}) SET n.name = $usedName",  map);  neo4jTemplate.toExecutableQuery(  PersonEntity.class,  queryFragmentsAndParameters).getResults();  Optional<PersonEntity> person1 = neo4jTemplate.findById(userId, PersonEntity.class);  System.out.println("\n更新“新海诚”的name为曾用名“新津诚”(这是他的曾用名):\n"+person1);  // 更新②---------更新“新津诚”的name为“新海诚”  person.get().setName("新海诚");  neo4jTemplate.save(person.get());  Optional<PersonEntity> person2 = neo4jTemplate.findById(userId, PersonEntity.class);  System.out.println("\n更新“新津诚”的name为“新海诚”:\n"+person2);  
}

删除节点

@Test  
void deleteNodeAll() {  // 删除所有节点和关系(删除节点会响应删除关联关系)[也可以用cypherQuery执行,不再赘述]  neo4jTemplate.deleteAll(MovieEntity.class);  neo4jTemplate.deleteAll(PersonEntity.class);  
}

完整

/**  * 没有Repository情况下使用Neo4jTemplate操作数据  * @param neo4jTemplate  */  @Test  void TestNoRepository(@Autowired Neo4jTemplate neo4jTemplate){  // 删除所有节点和关系(删除节点会响应删除关联关系),避免后续创建节点重复影响  neo4jTemplate.deleteAll(MovieEntity.class);  neo4jTemplate.deleteAll(PersonEntity.class);  // 创建节点实体  MovieEntity movie = new MovieEntity("你的名字","影片讲述了男女高中生在梦中相遇,并寻找彼此的故事。");  // new Roles 参数1:Person实体,演员的出生年和姓名;参数2:演员名字列表(考虑到一个演员可能参演多个角色)  // 参数1是目标关系实体节点 参数2是关系属性  Roles roles1 = new Roles(new PersonEntity(1998,"上白石萌音"), Collections.singletonList("宫水三叶"));  Roles roles2 = new Roles(new PersonEntity(1993,"神木隆之介"), Collections.singletonList("立花泷"));  PersonEntity director = new PersonEntity(1973,"新海诚");  // 添加movie的演员实体,加入(参演)关系  movie.getActorsAndRoles().add(roles1);  movie.getActorsAndRoles().add(roles2);  movie.getDirectors().add(director);  // 存入图数据库持久化  neo4jTemplate.save(movie);  // 查询(不太推荐用Neo4jTemplate进行过滤查询,因为需要手动写cypherQuery,需要开发人员了解一下cypherQuery的写法)  Optional<PersonEntity> person;  // 1. 通过id查询  person = neo4jTemplate.findById(12, PersonEntity.class);  System.out.println("id为12号的Person节点:\n"+person);  // 2. 通过属性查询节点,如name 需要手写cypherQuery语句  Map<String,Object> map = new HashMap<>();  map.put("name","新海诚");  // 两种写法都对,看个人喜好 n是一个变量随意取,{}或者where填写query的filter过滤条件  person = neo4jTemplate.findOne("MATCH (n:Person {name: $name}) RETURN n",map, PersonEntity.class);  
//        person = neo4jTemplate.findOne("MATCH (n:Person) WHERE n.name = $name RETURN n",map, PersonEntity.class);  System.out.println("\n查询名字为新海诚的Person节点:\n"+person);  // 3. 通过属性关系查询节点  map = new HashMap<>();  map.put("roles",Collections.singletonList("宫水三叶"));  // 方法1.使用toExecutableQuery查询  QueryFragmentsAndParameters parameters = new QueryFragmentsAndParameters(  "MATCH (person:Person) -[ relation:ACTED_IN]-> (movie:Movie) \n" +  "WHERE relation.roles = $roles\n" +  "RETURN person",map);  List<PersonEntity> roles = neo4jTemplate.toExecutableQuery(PersonEntity.class, parameters).getResults();  // 方法2.使用findOne查询  
//        Optional<PersonEntity> roles = neo4jTemplate.findOne(  
//                "MATCH (person:Person) -[ relation:ACTED_IN]-> (movie:Movie) \n" +  
//                "WHERE relation.roles = $roles\n" +  
//                "RETURN person",map,PersonEntity.class);  System.out.println("\n查询角色为“宫水三叶”的演员:\n"+roles);  Long userId = person.get().getId();// 记录当前查询的"新海诚"的节点id  // 更新①---------更新“新海诚”的name为曾用名“新津诚”(这是他的曾用名)  map.put("name","新海诚");  map.put("usedName","新津诚");  QueryFragmentsAndParameters queryFragmentsAndParameters =  new QueryFragmentsAndParameters(  "MATCH (n:Person{name: $name}) SET n.name = $usedName",  map);  neo4jTemplate.toExecutableQuery(  PersonEntity.class,  queryFragmentsAndParameters).getResults();  Optional<PersonEntity> person1 = neo4jTemplate.findById(userId, PersonEntity.class);  System.out.println("\n更新“新海诚”的name为曾用名“新津诚”(这是他的曾用名):\n"+person1);  // 更新②---------更新“新津诚”的name为“新海诚”  person.get().setName("新海诚");  neo4jTemplate.save(person.get());  Optional<PersonEntity> person2 = neo4jTemplate.findById(userId, PersonEntity.class);  System.out.println("\n更新“新津诚”的name为“新海诚”:\n"+person2);  }

使用 repository 对图数据进行 CRUD

创建 Repository

新建 repository 包,创建 PersonRepositoryMovieRepository

package cn.tmesh.neo4jdemo.repository;  import cn.tmesh.neo4jdemo.entity.MovieEntity;  
import org.springframework.data.neo4j.repository.Neo4jRepository;  
import org.springframework.stereotype.Repository;  import java.util.List;  @Repository  
public interface MovieRepository extends Neo4jRepository<MovieEntity, Long> {  //    @Query("MATCH (n:Movie) WHERE id(n) = $0 RETURN n") 这种方法是自己写Query语句进行查询  List<MovieEntity> findMovieEntitiesById(Long id);  MovieEntity findMovieEntityByTitle(String title);  
}
package cn.tmesh.neo4jdemo.repository;  import cn.tmesh.neo4jdemo.entity.PersonEntity;  
import org.springframework.data.neo4j.repository.Neo4jRepository;  
import org.springframework.stereotype.Repository;  @Repository  
public interface PersonRepository extends Neo4jRepository<PersonEntity, Long> {  PersonEntity findPersonEntityByName(String name);  
}

创建节点和关系

@Test  
void createNode() {  // 创建节点  MovieEntity movie = new MovieEntity("你的名字","影片讲述了男女高中生在梦中相遇,并寻找彼此的故事。");  Roles roles1 = new Roles(new PersonEntity(1998,"上白石萌音"), Collections.singletonList("宫水三叶"));  Roles roles2 = new Roles(new PersonEntity(1993,"神木隆之介"), Collections.singletonList("立花泷"));  PersonEntity director = new PersonEntity(1973,"新海诚");  // 添加关系  movie.getActorsAndRoles().add(roles1);  movie.getActorsAndRoles().add(roles2);  movie.getDirectors().add(director);  // 存入图数据库持久化  movieRepository.save(movie);  
}

查询

根据 Person 的名字查询对应节点

@Test  
void selectNode() {  // 查询  PersonEntity person = personRepository.findPersonEntityByName("上白石萌音");  System.out.println("查询名字为“上白石萌音”的PersonEntity:"+person);  MovieEntity movieQueried = movieRepository.findMovieEntityByTitle("你的名字");  System.out.println("查询名字为“你的名字”的MovieEntity:"+movieQueried);  
}

更新

@Test  
void updateNode() {  PersonEntity person = personRepository.findPersonEntityByName("上白石萌音");  System.out.println("查询名字为“上白石萌音”的PersonEntity:"+person);  // 更新(更新主要是三步:1.获取实体id;2.修改实体属性;3.更新实体)  // 注意:repository的save方法【对应的实体若id一致】则为修改,否则为新建。  Long personId = person.getId();  person.setBorn(1997);  personRepository.save(person);  person = personRepository.findPersonEntityByName("上白石萌音");  System.out.println(personId == person.getId()?"\n更新“上白石萌音”出生日期为1997信息成功!:\n"+person:"更新信息失败!");  
}

删除

@Test  
void deleteNodeAll() {  // 删除所有节点和关系  movieRepository.deleteAll();  personRepository.deleteAll();  
}

完整

/**  * 使用repository操作图数据  */  
@Test  
void testByRepository(@Autowired MovieRepository movieRepository, @Autowired PersonRepository personRepository){  // 删除所有节点和关系(删除节点会响应删除关联关系),避免后续创建节点重复影响  movieRepository.deleteAll();  personRepository.deleteAll();  // 创建节点  MovieEntity movie = new MovieEntity("你的名字","影片讲述了男女高中生在梦中相遇,并寻找彼此的故事。");  Roles roles1 = new Roles(new PersonEntity(1998,"上白石萌音"), Collections.singletonList("宫水三叶"));  Roles roles2 = new Roles(new PersonEntity(1993,"神木隆之介"), Collections.singletonList("立花泷"));  PersonEntity director = new PersonEntity(1973,"新海诚");  // 添加关系  movie.getActorsAndRoles().add(roles1);  movie.getActorsAndRoles().add(roles2);  movie.getDirectors().add(director);  // 存入图数据库持久化  movieRepository.save(movie);  // 查询  PersonEntity person = personRepository.findPersonEntityByName("上白石萌音");  System.out.println("\n查询名字为“上白石萌音”的PersonEntity:\n"+person);  MovieEntity movieQueried = movieRepository.findMovieEntityByTitle("你的名字");  System.out.println("\n查询名字为“你的名字”的MovieEntity:\n"+movieQueried);  // 更新(更新主要是三步:1.获取实体id;2.修改实体属性;3.更新实体)  // 注意:repository的save方法【对应的实体若id一致】则为修改,否则为新建。  Long personId = person.getId();  person.setBorn(1997);  personRepository.save(person);  person = personRepository.findPersonEntityByName("上白石萌音");  System.out.println(personId == person.getId()?"\n更新“上白石萌音”出生日期为1997信息成功!:\n"+person:"更新信息失败!");  
}
http://www.sczhlp.com/news/4694/

相关文章:

  • 单链表的定义与基本操作
  • 2025年08月随便做做
  • select与epoll实现差异的理解
  • neo4j 安装
  • 2025.8.3.模拟赛题目及代码记录
  • neo4j 介绍
  • J1939传输协议功能
  • P11620 [Ynoi Easy Round 2025] TEST_34 线性基 随机 线段树
  • [题解]P1516 青蛙的约会
  • python模块之pandas
  • Flora:实现任意长度与规模上下文构建的创新方法
  • Multisim14安装教程超详细保姆级包含下载、安装、汉化、激活
  • 补题祭
  • 函数进阶与按键
  • python对获取网页内容方法
  • Day7 列表类型内置方法 元组类型内置方法 字典类型内置方法 集合类型内置方法 数据类型总结+深浅拷贝
  • instanceof,抽象类和接口
  • LazyVim键位笔记(按使用场景分类)
  • GPIO读取函数
  • 数据结构1——线性表
  • 8.3
  • 实用指南:Windows环境下安装Python和PyCharm
  • 题解:P9358 [ICPC 2022 Xian R] Bridge
  • 《T30天正暖通 v1.0 全流程安装指南,一次搞定 AutoCAD 插件配置!》
  • MapReducedYARN配置
  • Animate.css - 轻松实现网页动画效果
  • nginx 配置 - Lafite
  • 深入解析:Python day18
  • pygame小游戏打飞机_7射中检测
  • 性能优化:SQL优化案例:分页查询