跳到主要内容

快速开始

DeepSeek V3 中英对照 Getting started

我们为 SDN 提供了一个 Spring Boot 启动器。请通过您的依赖管理包含该启动器模块,并配置要使用的 bolt URL,例如 spring.neo4j.uri=bolt://localhost:7687。该启动器假设服务器已禁用身份验证。由于 SDN 启动器依赖于 Java 驱动程序的启动器,因此关于配置的所有内容在此处同样适用。有关可用属性的参考,请使用您的 IDE 在 spring.neo4j 命名空间中的自动补全功能。

SDN 支持

  • 众所周知且易于理解的命令式编程模型(非常类似于 Spring Data JDBC 或 JPA)

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

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

准备数据库

对于这个示例,我们继续使用 电影图,因为它随每个 Neo4j 实例免费提供。

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

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

现在你可以访问 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 -
bash

这将创建一个新的文件夹 Neo4jSpringBootExample。由于这个 starter 尚未在 initializer 上提供,你需要手动将以下依赖添加到你的 pom.xml 中:

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

对于现有项目,您也需要手动添加依赖项。

使用 Gradle 创建 Spring Boot 项目

思路相同,只是生成一个 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 -
bash

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

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

对于已有项目,您也需要手动添加依赖。

配置项目

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

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

这是连接 Neo4j 实例所需的最基本内容。

备注

当你使用此启动器时,无需添加任何驱动程序的编程配置。SDN 仓库将自动启用。

配置 Neo4j Cypher-DSL

根据你运行应用程序的 Neo4j 版本,建议配置 Neo4j Cypher-DSL 运行的方言。默认使用的方言是针对 Neo4j 4.4 的,作为 Neo4j 的长期支持(LTS)版本。这可以通过定义一个 Cypher-DSL Configuration bean 来更改。

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

尽管 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
}
java
  • Spring Data Neo4j 使用了 Spring Data Commons 及其反射功能,因此你至少需要将你的领域包开放给 spring.core

  • 我们在此假设 your.domain 也包含了仓库:这些仓库必须被导出以便 spring.beansspring.contextspring.data.commons 可以访问。如果你不想将它们导出给所有模块,可以将它们限制在这些模块中。

创建你的领域

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

  • 将您的图映射到对象

  • 提供对这些对象的访问

示例 Node-Entity

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

备注

SDN 支持 Neo4j Java 驱动程序支持的所有数据类型,请参阅 将 Neo4j 类型映射为本地语言类型 章节中的 "Cypher 类型系统"。未来版本将支持更多的转换器。

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
}
java
  • @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;
}
}
}
java
  • 这是应用程序代码使用的构造函数。它将 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 的一部分。大多数情况下,移除某些东西比后续添加东西要困难得多。此外,使用特定于存储的方法会将你的存储细节泄漏到你的领域中。从性能角度来看,这并没有任何损失。

一个适用于上述任何电影实体的响应式仓库如下所示:

import reactor.core.publisher.Mono;

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

public interface MovieRepository extends ReactiveNeo4jRepository<MovieEntity, String> {

Mono<MovieEntity> findOneByTitle(String title);
}
java
提示

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