Getting started

我们为 SDN 提供了一个 Spring Boot starter。请通过您的依赖项管理工具包含 starter 模块,并配置要使用的 bolt URL,例如 spring.neo4j.uri=bolt://localhost:7687。starter 假设服务器已禁用身份验证。由于 SDN starter 取决于 Java 驱动程序的 starter,因此那里所说的所有有关配置的内容同样适用于这里。对于可用属性的参考,请在 spring.neo4j 名称空间中使用您的 IDE 的自动完成功能。 SDN 支持

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

  • 基于 Reactive Streams 的反应式编程,包括对 reactive transactions 的完全支持。

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

Prepare the database

对于此示例,我们停留在 movie graph 中,因为它免费附带在每个 Neo4j 实例中。

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

Start a local Neo4j instance inside Docker.
docker run --publish=7474:7474 --publish=7687:7687 -e 'NEO4J_AUTH=neo4j/secret' neo4j:{docs-neo4j-docker-version}

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

Create a new Spring Boot project

设置 Spring Boot 项目最简单的方法是 start.spring.io(它也集成在主要的 IDE 中,如果您不想使用网站)。

选择“Spring Web Starter”以获取创建基于 Spring 的 web 应用程序所需的所有依赖项。Spring Initializr 将为您创建一个有效项目结构,其中包含所有文件和设置,以适用于所选的构建工具。

Using Maven

您可以针对 Spring Initializr 发出 curl 请求以创建基本 Maven 项目:

Create a basic Maven project with the Spring Initializr
curl https://start.spring.io/starter.tgz \
  -d dependencies=webflux,data-neo4j  \
  -d bootVersion={spring-boot-version} \
  -d baseDir=Neo4jSpringBootExample \
  -d name=Neo4j%20SpringBoot%20Example | tar -xzvf -

这将创建一个新文件夹 Neo4jSpringBootExample。由于该 starter 尚未在初始器上,因此您必须手动将以下依赖项添加到您的 pom.xml 中:

Inclusion of the spring-data-neo4j-spring-boot-starter in a Maven project
<dependency>
	<groupId>{groupIdStarter}</groupId>
	<artifactId>{artifactIdStarter}</artifactId>
</dependency>

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

Using Gradle

思路是一样的,只需生成一个 Gradle 项目:

Create a basic Gradle project with the Spring Initializr
curl https://start.spring.io/starter.tgz \
  -d dependencies=webflux,data-neo4j \
  -d type=gradle-project \
  -d bootVersion={spring-boot-version} \
  -d baseDir=Neo4jSpringBootExampleGradle \
  -d name=Neo4j%20SpringBoot%20Example | tar -xzvf -

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

Inclusion of the spring-data-neo4j-spring-boot-starter in a Gradle project
dependencies {
    implementation '{groupIdStarter}:{artifactIdStarter}'
}

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

Configure the project

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

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

这是连接到 Neo4j 实例所需的最低要求。

使用此启动器时,没有必要附加驱动中的任何编程配置。SDN 存储库将通过此启动器自动启用。

Configure Neo4j Cypher-DSL

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

Make Cypher-DSL use the Neo4j 5 dialect
@Bean
Configuration cypherDslConfiguration() {
	return Configuration.newConfig()
                .withDialect(Dialect.NEO4J_5).build();
}

虽然 Spring Data Neo4j 尽最大努力与其 Neo4j 5 和默认方言的组合兼容,但始终建议显式定义方言。例如,它将导致查询得到更多优化,并对较新版本的 Neo4j 使用 elementId()

Running on the Module-Path

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

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

A module-info.java in a project supposed to use Spring Data Neo4j on the module path
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)
}
1 Spring Data Neo4j 使用 Spring Data Commons 及其反射功能,因此您需要至少对 spring.core 打开您的域包。
2 我们在此假设 your.domain 还包含存储库:这些存储库必须导出才能供 spring.beansspring.contextspring.data.commons 访问。如果您不想将它们导出到世界中,则可以将它们限制在这些模块中。

Create your domain

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

  • 将您的图形映射到对象

  • Provide access to those

Example Node-Entity

SDN 完全支持不可修改的实体,包括对于 Java 和 Kotlin 中的 data 类。因此,我们将重点放在不可变实体上,[movie-entity] 展示了这样的实体。

SDN 支持 Neo4j Java 驱动程序支持的所有数据类型,请参阅“Cypher 类型系统”一章中的 Map Neo4j types to native language types。未来的版本将支持其他转换器。

MovieEntity.java
Unresolved include directive in modules/ROOT/pages/getting-started.adoc - include::example$documentation/domain/MovieEntity.java[]
1 @Node 用于将此类标记为托管实体。它还用于配置 Neo4j 标签。如果您只使用简单的 @Node ,则标签默认为类名。
2 每个实体都必须有 ID。此处显示的电影类使用属性 title 作为唯一的业务键。如果您没有这样的唯一键,则可以使用 @Id@GeneratedValue 的组合来配置 SDN 以使用 Neo4j 的内部 ID。我们还提供 UUID 生成器。
3 这展示了 @Property 作为将字段的名称用于图属性的名称不同于 graph 的方式。
4 这定义了与类型 PersonEntity 的类和关系类型 ACTED_IN 的关系
5 这是应用程序代码要使用的构造函数。

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

如果您找不到好的业务键或不想对 ID 使用生成器,以下是使用内部生成 id 以及常规构造函数和 SDN 使用的所谓的 wither- 方法的相同实体:

MovieEntity.java
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;
		}
	}
}
1 这是应用程序代码要使用的构造函数。它将 ID 设置为 null,因为包含内部 ID 的字段永远不应受到处理。
2 这是所谓的 wither ,针对 id 属性。它创建一个新实体并相应地设置字段,而不修改原始实体,从而使其不可变。

当然,你可以使用 SDN 和 Kotlin,并使用 Kotlin 的数据类来模拟你的域。如果您希望或需要完全使用 Java,则 Project Lombok 是一个选择。

Declaring Spring Data repositories

您基本上有两个选择:您可以使用 SDN 以与存储无关的方式工作,并使域特定扩展其中一个

  • org.springframework.data.repository.Repository

  • org.springframework.data.repository.CrudRepository

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

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

相应选择命令式和响应式。

虽然在技术上没有禁止,但建议不要在同一应用程序中混合命令式和反应式数据库访问。我们不会对这样的场景提供支持。

另一个选择是建立在特定于存储的实现上,并获得开箱即用的所有支持方法。这种方法的优点也是其最大的缺点:一旦发布,所有这些方法都将成为您 API 的一部分。大多数情况下,删除一些东西比事后添加更困难。此外,使用特定于存储的内容会将您的存储泄露到域中。从性能的角度来看,没有损失。

适用于上述任何影片实体的响应式存储库如下所示:

MovieRepository.java
Unresolved include directive in modules/ROOT/pages/getting-started.adoc - include::example$documentation/domain/MovieRepository.java[]

使用 reactor.test.StepVerifier 测试响应式代码。请查看相应的 documentation of Project Reactor 或参见我们的示例代码。