跳到主要内容
版本:8.1.1

快速上手

Doubao 中英对照 Getting started

我们为 SDN 提供了一个 Spring Boot 起步依赖。请通过您的依赖管理引入该起步模块,并配置要使用的 bolt URL,例如 spring.neo4j.uri=bolt://localhost:7687。该起步依赖假定服务器已禁用身份验证。由于 SDN 起步依赖依赖 Java 驱动的起步依赖,那里关于配置的所有说明在这里同样适用。有关可用属性的参考,请在 spring.neo4j 命名空间下使用您 IDE 的自动补全。

SDN 支持

  • 广为人知、易于理解的命令式编程模型(很像 Spring Data JDBC 或 JPA)

  • 基于 Reactive Streams 的响应式编程,包括对响应式事务的全面支持。

这些都包含在同一个二进制文件中。响应式编程模型在数据库侧需要 4+ 的 Neo4j 服务器,另一方面需要响应式的 Spring。

准备数据库​

在本例中,我们使用电影图,因为每个 Neo4j 实例都自带它。

如果您没有正在运行的数据库但安装了 Docker,请运行:

docker run --publish=7474:7474 --publish=7687:7687 -e 'NEO4J_AUTH=neo4j/secret' neo4j:5

现在您可以访问 http://localhost:7474。上面的命令将服务器密码设置为 secret。请注意提示符中准备好运行的命令(:play movies)。执行它即可用一些测试数据填充您的数据库。

创建一个新的 Spring Boot 项目​

搭建 Spring Boot 项目最简单的方式是 start.spring.io(它也集成在主流 IDE 中,如果您不想使用网站的话)。

选择 "Spring Web Starter" 以获得创建基于 Spring 的 Web 应用所需的全部依赖。Spring Initializr 会为您创建有效的项目结构,为所选构建工具备好所有文件和设置。

使用 Maven​

您可以向 Spring Initializer 发出一个 curl 请求来创建一个基本的 Maven 项目:

curl https://start.spring.io/starter.tgz \
-d dependencies=webflux,data-neo4j \
-d bootVersion=3.2.0 \
-d baseDir=Neo4jSpringBootExample \
-d name=Neo4j%20SpringBoot%20Example | tar -xzvf -

这将创建一个新文件夹 Neo4jSpringBootExample。由于该起步依赖尚未出现在 initializer 中,您需要手动将以下依赖添加到您的 pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>

如果是既有项目,您同样需要手动添加该依赖。

使用 Gradle​

思路相同,只需生成一个 Gradle 项目:

curl https://start.spring.io/starter.tgz \
-d dependencies=webflux,data-neo4j \
-d type=gradle-project \
-d bootVersion=3.2.0 \
-d baseDir=Neo4jSpringBootExampleGradle \
-d name=Neo4j%20SpringBoot%20Example | tar -xzvf -

Gradle 的依赖如下所示,必须添加到 build.gradle:

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
}

如果是既有项目,您同样需要手动添加该依赖。

配置项目​

现在在您喜欢的 IDE 中打开其中任一项目。找到 application.properties 并配置您的 Neo4j 凭据:

spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=verysecret

这是连接到 Neo4j 实例所需的最低配置。

备注

使用该起步依赖时,无需添加任何对驱动的编程式配置。SDN 仓库会由此起步依赖自动启用。

配置 Neo4j Cypher-DSL​

根据您运行应用所使用的 Neo4j 版本,建议配置 Neo4j Cypher-DSL 运行所采用的方言。默认使用的方言以 Neo4j 5 作为 Neo4j 的 LTS 版本为目标。它兼容 Neo4j 5.23+ 和 Neo4j 2025.x。可以通过定义一个 Cypher-DSL Configuration bean 来更改。

@Bean
Configuration cypherDslConfiguration() {
return Configuration.newConfig()
.withDialect(Dialect.NEO4J_4).build();
}
备注

尽管 Spring Data Neo4j 尽力兼容 Neo4j 5 与默认方言的组合,但始终建议显式定义方言。例如,这会带来更优化的查询,并在较新的 Neo4j 版本上使用 elementId()。

在模块路径上运行​

Spring Data Neo4j 可以在模块路径上运行。它的自动模块名是 spring.data.neo4j。由于当前 Spring Data 构建设置中的限制,它本身不提供模块。因此,它使用一个自动但稳定的模块名。不过,它确实依赖一个模块化的库(Cypher-DSL)。由于上述限制而没有 module-info.java,我们无法代您表达对该库的依赖要求。

因此,在模块路径上运行 Spring Data Neo4j 6.1+ 时,您项目中所需的最小 module-info.java 如下:

module your.module {

requires org.neo4j.cypherdsl.core;

requires spring.data.commons;
requires spring.data.neo4j;

opens your.domain to spring.core; 1

exports your.domain; 2
}
  • Spring Data Neo4j 使用 Spring Data Commons 及其反射能力,因此您至少需要向 spring.core 开放您的领域包。

  • 这里我们假设 your.domain 中还包含仓库:这些必须导出,才能被 spring.beans、spring.context 和 spring.data.commons 访问。如果您不想把它们导出给全局,可以将访问权限限制为这些模块。

创建您的领域​

我们的领域层应完成两件事:

  • 将您的图映射为对象

  • 提供对这些对象的访问

示例节点实体​

SDN 完全支持不可变实体,无论是 Java 还是 Kotlin 中的 data 类。因此,这里我们将重点放在不可变实体上,MovieEntity.java 展示了这样一个实体。

备注

SDN 支持 Neo4j Java 驱动支持的所有数据类型,参见“Cypher 类型系统”一章中的将 Neo4j 类型映射为原生语言类型。未来版本将支持更多转换器。

import java.util.ArrayList;
import java.util.List;

import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Property;
import org.springframework.data.neo4j.core.schema.Relationship;
import org.springframework.data.neo4j.core.schema.Relationship.Direction;

@Node("Movie") 1
public class MovieEntity {

@Id 2
private final String title;

@Property("tagline") 3
private final String description;

@Relationship(type = "ACTED_IN", direction = Direction.INCOMING) 4
private List<Roles> actorsAndRoles = new ArrayList<>();

@Relationship(type = "DIRECTED", direction = Direction.INCOMING)
private List<PersonEntity> directors = new ArrayList<>();

public MovieEntity(String title, String description) { 5
this.title = title;
this.description = description;
}

// Getters omitted for brevity

}
  • @Node 用于将该类标记为受管实体。它也用于配置 Neo4j 标签。如果您只使用普通的 @Node,标签默认为类名。

  • 每个实体都必须有一个 id。这里展示的 movie 类使用属性 title 作为唯一业务键。如果您没有这样的唯一键,可以使用 @Id 与 @GeneratedValue 的组合来配置 SDN 使用 Neo4j 的内部 id。我们还提供 UUID 生成器。

  • 这里展示了 @Property,作为为字段使用与图属性不同名称的一种方式。

  • 这里定义了到类型为 PersonEntity 的类的关系,以及关系类型 ACTED_IN

  • 这是供您应用代码使用的构造函数。

作为一般性说明:使用内部生成 id 的不可变实体多少有些矛盾,因为 SDN 需要一种方式来用数据库生成的值设置该字段。

如果您找不到合适的业务键,或不想为 id 使用生成器,下面是同一个实体,它使用内部生成 id,并配合一个常规构造函数和一个所谓的 wither 方法,供 SDN 使用:

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.Property;

import org.springframework.data.annotation.PersistenceConstructor;

@Node("Movie")
public class MovieEntity {

@Id @GeneratedValue
private Long id;

private final String title;

@Property("tagline")
private final String description;

public MovieEntity(String title, String description) { 1
this.id = null;
this.title = title;
this.description = description;
}

public MovieEntity withId(Long id) { 2
if (this.id.equals(id)) {
return this;
} else {
MovieEntity newObject = new MovieEntity(this.title, this.description);
newObject.id = id;
return newObject;
}
}
}
  • 这是供您应用代码使用的构造函数。它将 id 设置为 null,因为包含内部 id 的字段绝不应被修改。

  • 这是针对 id 属性的所谓 wither。它创建一个新实体并相应设置该字段,而不修改原实体,从而使其不可变。

您当然可以将 SDN 与 Kotlin 一起使用,并用 Kotlin 的数据类为您的领域建模。如果您希望或需要纯粹使用 Java,Project Lombok 是一个替代方案。

声明 Spring Data 仓库​

在这里您基本上有两个选择:您可以以与存储无关的方式使用 SDN,让您特定的领域继承以下之一:

  • org.springframework.data.repository.Repository

  • org.springframework.data.repository.CrudRepository

  • org.springframework.data.repository.reactive.ReactiveCrudRepository

  • org.springframework.data.repository.reactive.ReactiveSortingRepository

据此选择命令式或响应式。

注意

虽然技术上并不禁止,但不建议在同一个应用中混用命令式和响应式数据库访问。我们不会支持此类场景。

另一个选择是采用特定存储的实现,并开箱即用地获得我们支持的所有方法。这种方法的优点同时也是它最大的缺点:一旦发布,所有这些方法都将成为您 API 的一部分。大多数时候,去掉某些东西比之后再添加更难。此外,使用存储特定的内容会把您的存储泄漏到领域中。从性能角度看,这没有任何损失。

一个适配上述任一 movie 实体的响应式仓库如下所示:

import reactor.core.publisher.Mono;

import org.springframework.data.neo4j.repository.ReactiveNeo4jRepository;

public interface MovieRepository extends ReactiveNeo4jRepository<MovieEntity, String> {

Mono<MovieEntity> findOneByTitle(String title);

}
提示

测试响应式代码使用 reactor.test.StepVerifier。请参阅 Project Reactor 的相应文档,或查看我们的示例代码。