本指南介绍如何使用 Spring Data Neo4j 构建应用,将数据存入图数据库 Neo4j 并查询出来。
将构建什么
使用 Neo4j 的 NoSQL 图数据存储,搭建 Neo4j 服务器,存储实体和关系,并编写查询。原文概述称其为“嵌入式 Neo4j 服务器”;下文实际演示的是独立启动的服务器或 Docker 容器,通过 Bolt 连接。
需要什么
- 约15分钟。
- 喜欢的文本编辑器或 IDE。
- Java 17 或更新版本。
- Gradle 7.5+ 或 Maven 3.5+。
也可以直接将代码导入 IDE:Spring Tool Suite(STS)、IntelliJ IDEA 或 VSCode。
如何完成本指南
与多数 Spring 入门指南一样,可以从零开始完成每一步,也可以跳过已经熟悉的基础设置。两种方式最终都能得到应用代码。
- 从零开始:进入“从 Spring Initializr 开始”一节。
- 跳过基础设置:
- 下载并解压源码仓库,或使用 Git 克隆:
git clone https://github.com/spring-guides/gs-accessing-data-neo4j.git。 - 切换到
gs-accessing-data-neo4j/initial目录。 - 跳到“定义一个简单实体”一节。
- 下载并解压源码仓库,或使用 Git 克隆:
完成后,可用 gs-accessing-data-neo4j/complete 中的代码核对结果。
从 Spring Initializr 开始
可使用预初始化项目,点击 Generate 下载 ZIP。该项目已经按本指南示例配置。
若手动初始化:
- 访问 start.spring.io。这个服务会引入应用所需依赖,并完成大部分设置。
- 选择 Gradle 或 Maven,以及开发语言;本指南假定选择 Java。
- 点击 Dependencies,选择 Spring Data Neo4j。
- 点击 Generate。
- 下载生成的 ZIP,它包含按所选配置建立的应用。
若 IDE 集成了 Spring Initializr,也可以在 IDE 内完成。同样可在 GitHub fork 项目,再用 IDE 或编辑器打开。
启动 Neo4j 服务器
构建应用前,先设置 Neo4j 服务器。Neo4j 提供可免费安装的开源服务器,也可以通过 Docker 运行。
在已经安装 Homebrew 的 Mac 上,执行:
$ brew install neo4j
其他安装方式见 Neo4j Community Edition 下载页面。
安装后,以默认设置启动:
$ neo4j start
应看到类似以下的输出:
Starting Neo4j.
Started neo4j (pid 96416). By default, it is available at http://localhost:7474/
There may be a short delay until the server is ready.
See /usr/local/Cellar/neo4j/<version>/libexec/logs/neo4j.log for current status.
Neo4j 默认用户名和密码都是 neo4j,但要求更改新账户的密码。打开 Neo4j Browser,用默认凭据登录,就会收到设置新密码的提示。本指南使用 spring+neo4j 作为演示密码,不要在生产中这样做。完成后即可继续。
也可以使用 Neo4j Docker 镜像运行,通过 NEO4J_AUTH 环境变量修改密码:
docker run \
--publish=7474:7474 --publish=7687:7687 \
--volume=$HOME/neo4j/data:/data \
--env NEO4J_AUTH=neo4j/spring+neo4j \
neo4j
定义一个简单实体
Neo4j 同时记录实体及其关系,两者同等重要。假设要为每个人保存一条记录,也希望追踪此人推荐了谁,本文称这些关系为 referrals。Spring Data Neo4j 只需一些简单注解就能表示这些内容。下面是 src/main/java/com/example/accessingdataneo4j/Person.java:
package com.example.accessingdataneo4j;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Relationship;
@Node
public class Person {
@Id
@GeneratedValue
private @Nullable Long id;
private String name;
private Person() {
// Empty constructor required as of Neo4j API 2.0.5
};
public Person(String name) {
this.name = name;
}
@Relationship(type = "REFERRED")
public Set<Person> referrals;
public void referred(Person person) {
if (referrals == null) {
referrals = new HashSet<>();
}
referrals.add(person);
}
public String toString() {
return this.name + " referred => "
+ Optional.ofNullable(this.referrals).orElse(
Collections.emptySet()).stream()
.map(Person::getName)
.collect(Collectors.toList());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Person 类有一个属性 name,并带有 @Node 注解;Neo4j 存储它时会创建新节点。类还有一个带 @Id 和 @GeneratedValue 注解的 id,Neo4j 在内部用它追踪数据。
另一个重要部分是 referrals 集合。它是简单的 Set<Person>,但标注了 @Relationship,表示集合的每个成员也应独立存在为一个 Person 节点。默认方向为 OUTGOING:REFERRED 关系从当前人指向他推荐的每个人。通过 referred() 方法,就能记录谁推荐了谁。最后,便捷的 toString() 方法用于输出此人姓名及推荐对象。
创建简单查询
Spring Data Neo4j 专注于在 Neo4j 存储数据,但也继承 Spring Data Commons 的功能,包括派生查询。因此,不必先学习 Neo4j 查询语言,只需编写几个方法,框架就能生成查询。
下面建立一个查询 Person 节点的接口,文件为 src/main/java/com/example/accessingdataneo4j/PersonRepository.java:
package com.example.accessingdataneo4j;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.data.neo4j.repository.Neo4jRepository;
public interface PersonRepository extends Neo4jRepository<Person, Long> {
@Nullable
Person findByName(String name);
List<Person> findByReferralsName(String name);
}
PersonRepository 扩展 Neo4jRepository,传入操作类型 Person。这个接口已经提供很多操作,包括标准的创建、读取、更新、删除(CRUD)。也可以通过声明方法签名定义其他查询。
findByName 寻找 Person 类型中 name 匹配的节点;findByReferralsName 则反向遍历 REFERRED 关系:给定一个人的姓名,返回所有曾推荐此人的 Person 节点。
访问 Neo4j 的权限
Neo4j Community Edition 要求访问凭据。可在 src/main/resources/application.properties 中设置:
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=spring+neo4j
其中包括默认用户名 neo4j,以及之前设置的演示密码 spring+neo4j。不要将真实凭据存入源码仓库。应在运行时通过 Spring Boot 属性覆盖机制配置。完成后即可将这些部分连接起来。
创建应用类
Spring Initializr 会生成一个简单应用类,本例位于 src/main/java/com/example/accessingdataneo4j/AccessingDataNeo4jApplication.java:
package com.example.accessingdataneo4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AccessingDataNeo4jApplication {
public static void main(String[] args) {
SpringApplication.run(AccessingDataNeo4jApplication.class, args);
}
}
@SpringBootApplication 是组合注解,包含:
@Configuration:将类标记为应用上下文的 bean 定义来源。@EnableAutoConfiguration:让 Spring Boot 根据 classpath、其他 bean 和各种属性设置添加 bean。例如 classpath 包含spring-webmvc时,会将应用识别为 web 应用,启用设置DispatcherServlet等关键行为。@ComponentScan:让 Spring 查找com/example包下的组件、配置和服务,从而发现控制器。
main() 使用 Spring Boot 的 SpringApplication.run() 启动应用。整个示例没有一行 XML,也没有 web.xml;配置使用纯 Java,不必手动处理底层基础设施。
只要仓储位于 @SpringBootApplication 类相同的包或子包内,Spring Boot 就会自动处理。要更细地控制注册,可使用 @EnableNeo4jRepositories。默认情况下,它扫描当前包内扩展 Spring Data 仓储接口的接口。如果项目结构涉及多个项目,框架未找到仓储,可通过 basePackageClasses=MyRepository.class,按类型安全地指定另一个扫描根包。
启动会显示日志输出,服务应在几秒内运行。接着自动装配之前定义的 PersonRepository 实例。Spring Data Neo4j 动态实现该接口,并加入满足接口要求的查询代码。
main 方法用 SpringApplication.run() 启动应用,并调用构建关系的 CommandLineRunner。这里创建 Greg、Roy、Craig 三个本地 Person 实例,最初仅在内存中,尚无人推荐别人。
先找到 Greg,记录他推荐了 Roy 和 Craig,再持久化 Greg。由于关系方向为 OUTGOING,此时只保存 Greg 向外的 REFERRED 关系。接着从 Neo4j 重新读取 Roy,确保得到节点最新状态,记录 Roy 推荐了 Craig,再持久化 Roy。Craig 在本例尚未推荐别人,因此不需要更多代码。
遍历每个人并将信息打印到控制台,就能查看结果。最后使用另一个反向查询,回答“谁推荐了 Craig?”完成后的应用类如下:
package com.example.accessingdataneo4j;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
@SpringBootApplication
@EnableNeo4jRepositories
public class AccessingDataNeo4jApplication {
private final static Logger log = LoggerFactory.getLogger(AccessingDataNeo4jApplication.class);
public static void main(String[] args) throws Exception {
SpringApplication.run(AccessingDataNeo4jApplication.class, args);
System.exit(0);
}
@Bean
CommandLineRunner demo(PersonRepository personRepository) {
return args -> {
personRepository.deleteAll();
Person greg = new Person("Greg");
Person roy = new Person("Roy");
Person craig = new Person("Craig");
List<Person> team = Arrays.asList(greg, roy, craig);
log.info("Before linking up with Neo4j...");
team.stream().forEach(person -> log.info("\t" + person.toString()));
personRepository.save(greg);
personRepository.save(roy);
personRepository.save(craig);
greg = personRepository.findByName(greg.getName());
greg.referred(roy);
greg.referred(craig);
personRepository.save(greg);
roy = personRepository.findByName(roy.getName());
roy.referred(craig);
personRepository.save(roy);
// Craig has no referrals yet
log.info("Lookup each person by name...");
team.stream().forEach(person -> log.info(
"\t" + personRepository.findByName(person.getName()).toString()));
List<Person> referrers = personRepository.findByReferralsName(craig.getName());
log.info("The following referred Craig...");
referrers.stream().forEach(person -> log.info("\t" + person.getName()));
};
}
}
构建可执行 JAR
可以通过 Gradle 或 Maven 从命令行运行应用,也可以构建包含全部依赖、类和资源的单个可执行 JAR,再运行它。这便于在开发生命周期中跨环境交付、版本管理和部署服务。
使用 Gradle 时,运行 ./gradlew bootRun;或者通过 ./gradlew build 构建 JAR,再运行:
java -jar build/libs/gs-accessing-data-neo4j-0.0.1-SNAPSHOT.jar
使用 Maven 时,运行 ./mvnw spring-boot:run;或者通过 ./mvnw clean package 构建 JAR,再运行:
java -jar target/gs-accessing-data-neo4j-0.0.1-SNAPSHOT.jar
应看到类似以下输出,也会有查询等其他日志:
Before linking up with Neo4j...
Greg referred => []
Roy referred => []
Craig referred => []
Lookup each person by name...
Greg referred => [Roy, Craig]
Roy referred => [Craig]
Craig referred => []
The following referred Craig...
Greg
Roy
输出显示最初没人推荐别人;建立关系后,可以看出谁推荐了谁;最后,反向查询找到了推荐某个特定人的全部人员。
总结
至此,你已设置 Neo4j 服务器,存储一些具有关系的简单实体,并建立快速查询。原文总结使用“嵌入式服务器”这一说法;按本指南实际步骤运行时,应用连接的是单独运行的 Neo4j。
若希望以少量工作为 Neo4j 仓储提供基于超媒体的 RESTful 前端,可继续阅读通过 REST 访问 Neo4j 数据。
另请参阅
若想编写新指南或贡献现有指南,可查看贡献指南。











暂无评论内容