Getting started
我们为 SDN 提供了一个 Spring Boot starter。请通过您的依赖项管理工具包含 starter 模块,并配置要使用的 bolt URL,例如 spring.neo4j.uri=bolt://localhost:7687
。starter 假设服务器已禁用身份验证。由于 SDN starter 取决于 Java 驱动程序的 starter,因此那里所说的所有有关配置的内容同样适用于这里。对于可用属性的参考,请在 spring.neo4j
名称空间中使用您的 IDE 的自动完成功能。
We provide a Spring Boot starter for SDN.
Please include the starter module via your dependency management and configure the bolt URL to use, for example spring.neo4j.uri=bolt://localhost:7687
.
The starter assumes that the server has disabled authentication.
As the SDN starter depends on the starter for the Java Driver, all things regarding configuration said there, apply here as well.
For a reference of the available properties, use your IDEs autocompletion in the spring.neo4j
namespace.
SDN 支持
SDN supports
-
The well known and understood imperative programming model (much like Spring Data JDBC or JPA)
-
Reactive programming based on Reactive Streams, including full support for reactive transactions.
这些都包含在同一个二进制文件中。另一方面,反应式编程模型需要一个数据库端的 4+ Neo4j 服务器,以及一个反应式 Spring。
Those are all included in the same binary. The reactive programming model requires a 4+ Neo4j server on the database side and reactive Spring on the other hand.
Prepare the database
对于此示例,我们停留在 movie graph 中,因为它免费附带在每个 Neo4j 实例中。
For this example, we stay within the movie graph, as it comes for free with every Neo4j instance.
如果您没有正在运行的数据库但已安装 Docker,请运行:
If you don’t have a running database but Docker installed, please run:
docker run --publish=7474:7474 --publish=7687:7687 -e 'NEO4J_AUTH=neo4j/secret' neo4j:{docs-neo4j-docker-version}
您现在可以访问 http://localhost:7474。上述命令将服务器的密码设置为 secret
。请注意提示符 (:play movies
) 中准备运行的命令。执行它以使用一些测试数据填充您的数据库。
You can now access http://localhost:7474.
The above command sets the password of the server to secret
.
Note the command ready to run in the prompt (:play movies
).
Execute it to fill your database with some test data.
Create a new Spring Boot project
设置 Spring Boot 项目最简单的方法是 start.spring.io(它也集成在主要的 IDE 中,如果您不想使用网站)。
The easiest way to set up a Spring Boot project is start.spring.io (which is integrated in the major IDEs as well, in case you don’t want to use the website).
选择“Spring Web Starter”以获取创建基于 Spring 的 web 应用程序所需的所有依赖项。Spring Initializr 将为您创建一个有效项目结构,其中包含所有文件和设置,以适用于所选的构建工具。
Select the "Spring Web Starter" to get all the dependencies needed for creating a Spring based web application. The Spring Initializr will take care of creating a valid project structure for you, with all the files and settings in place for the selected build tool.
Using Maven
您可以针对 Spring Initializr 发出 curl 请求以创建基本 Maven 项目:
You can issue a curl request against the Spring Initializer to create a basic Maven project:
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
中:
This will create a new folder Neo4jSpringBootExample
.
As this starter is not yet on the initializer, you will have to add the following dependency manually to your pom.xml
:
<dependency>
<groupId>{groupIdStarter}</groupId>
<artifactId>{artifactIdStarter}</artifactId>
</dependency>
对于现有项目,您也必须手动添加依赖项。
You would also add the dependency manually in case of an existing project.
Using Gradle
思路是一样的,只需生成一个 Gradle 项目:
The idea is the same, just generate a Gradle project:
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
中:
The dependency for Gradle looks like this and must be added to build.gradle
:
dependencies {
implementation '{groupIdStarter}:{artifactIdStarter}'
}
对于现有项目,您也必须手动添加依赖项。
You would also add the dependency manually in case of an existing project.
Configure the project
现在在您喜欢的 IDE 中打开其中任何一个项目。找到 application.properties
并配置您的 Neo4j 凭据:
Now open any of those projects in your favorite IDE.
Find application.properties
and configure your Neo4j credentials:
spring.neo4j.uri=bolt://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=verysecret
这是连接到 Neo4j 实例所需的最低要求。
This is the bare minimum of what you need to connect to a Neo4j instance.
使用此启动器时,没有必要附加驱动中的任何编程配置。SDN 存储库将通过此启动器自动启用。 |
It is not necessary to add any programmatic configuration of the driver when you use this starter. SDN repositories will be automatically enabled by this starter. |
Configure Neo4j Cypher-DSL
根据您运行应用程序的 Neo4j 版本,建议配置 Neo4j Cypher-DSL 运行的方言。使用的默认方言针对的是 Neo4j 4.4,作为 Neo4j 的 LTS 版本。这可以通过定义一个 Cypher-DSL Configuration
bean 来更改。
Depending on the Neo4j version you are running your application with,
it is advised to configure the dialect Neo4j Cypher-DSL runs with.
The default dialect that is used is targeting Neo4j 4.4. as the LTS version of Neo4j.
This can be changed by defining a Cypher-DSL Configuration
bean.
@Bean
Configuration cypherDslConfiguration() {
return Configuration.newConfig()
.withDialect(Dialect.NEO4J_5).build();
}
虽然 Spring Data Neo4j 尽最大努力与其 Neo4j 5 和默认方言的组合兼容,但始终建议显式定义方言。例如,它将导致查询得到更多优化,并对较新版本的 Neo4j 使用 |
Although Spring Data Neo4j tries it best to be compatible with also the combination of Neo4j 5 and a default dialect,
it is always recommend to explicitly define the dialect.
E.g. it will lead to more optimized queries and make use of |
Running on the Module-Path
Spring Data Neo4j 可以在模块路径上运行。其自动模块名称是 spring.data.neo4j
。由于当前 Spring Data 构建设置中的限制,它本身不提供模块。因此,它使用自动但稳定的模块名称。但是,它确实依赖于模块化库( Cypher-DSL)。由于上述限制,没有 module-info.java
,我们无法代表您表达对该库的要求。
Spring Data Neo4j can run on the module path. It’s automatic module name is spring.data.neo4j
.
It does not provide a module itself due to restrictions in the current Spring Data build setup.
Hence, it uses an automatic but stable module name. However, it does depend on
a modularized library (the Cypher-DSL). Without a module-info.java
due to
the restriction mentioned above, we cannot express the requirement for that library on your behalf.
因此,在模块路径上运行 Spring Data Neo4j 6.1+ 时,项目中最小需要的 module-info.java
如下所示:
Therefore, the minimal required module-info.java
in your project for running Spring Data Neo4j 6.1+ on the module path
is the following:
module-info.java
in a project supposed to use Spring Data Neo4j on the module pathmodule 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 uses Spring Data Commons and its reflective capabilities, so
you would need to open up your domain packages to spring.core at least. |
2 | We assume here that your.domain contains also repositories: Those must be exported to be accessible by
spring.beans , spring.context and spring.data.commons . If you don’t want to export them to the world,
you can restrict them to those modules. |
Create your domain
我们的域层应该完成两件事:
Our domain layer should accomplish two things:
-
Map your graph to objects
-
Provide access to those
Example Node-Entity
SDN 完全支持不可修改的实体,包括对于 Java 和 Kotlin 中的 data
类。因此,我们将重点放在不可变实体上,[movie-entity] 展示了这样的实体。
SDN fully supports unmodifiable entities, for both Java and data
classes in Kotlin.
Therefore, we will focus on immutable entities here, [movie-entity] shows a such an entity.
SDN 支持 Neo4j Java 驱动程序支持的所有数据类型,请参阅“Cypher 类型系统”一章中的 Map Neo4j types to native language types。未来的版本将支持其他转换器。 |
SDN supports all data types the Neo4j Java Driver supports, see Map Neo4j types to native language types inside the chapter "The Cypher type system". Future versions will support additional converters. |
Unresolved include directive in modules/ROOT/pages/getting-started.adoc - include::example$documentation/domain/MovieEntity.java[]
1 | @Node is used to mark this class as a managed entity.
It also is used to configure the Neo4j label.
The label defaults to the name of the class, if you’re just using plain @Node . |
2 | Each entity has to have an id.
The movie class shown here uses the attribute title as a unique business key.
If you don’t have such a unique key, you can use the combination of @Id and @GeneratedValue
to configure SDN to use Neo4j’s internal id.
We also provide generators for UUIDs. |
3 | This shows @Property as a way to use a different name for the field than for the graph property. |
4 | This defines a relationship to a class of type PersonEntity and the relationship type ACTED_IN |
5 | This is the constructor to be used by your application code. |
作为一般性说明:使用内部生成 id 的不可变实体有点矛盾,因为 SDN 需要一种方法来设置包含数据库生成的值的字段。
As a general remark: immutable entities using internally generated ids are a bit contradictory, as SDN needs a way to set the field with the value generated by the database.
如果您找不到好的业务键或不想对 ID 使用生成器,以下是使用内部生成 id 以及常规构造函数和 SDN 使用的所谓的 wither- 方法的相同实体:
If you don’t find a good business key or don’t want to use a generator for IDs, here’s the same entity using the internally generated id together with a regular constructor and a so called wither-Method, that is used by 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;
}
}
}
1 | This is the constructor to be used by your application code. It sets the id to null, as the field containing the internal id should never be manipulated. |
2 | This is a so-called wither for the id -attribute.
It creates a new entity and sets the field accordingly, without modifying the original entity, thus making it immutable. |
当然,你可以使用 SDN 和 Kotlin,并使用 Kotlin 的数据类来模拟你的域。如果您希望或需要完全使用 Java,则 Project Lombok 是一个选择。
You can of course use SDN with Kotlin and model your domain with Kotlin’s data classes. Project Lombok is an alternative if you want or need to stay purely within Java.
Declaring Spring Data repositories
您基本上有两个选择:您可以使用 SDN 以与存储无关的方式工作,并使域特定扩展其中一个
You basically have two options here: you can work in a store-agnostic fashion with SDN and make your domain specific extend one of
-
org.springframework.data.repository.Repository
-
org.springframework.data.repository.CrudRepository
-
org.springframework.data.repository.reactive.ReactiveCrudRepository
-
org.springframework.data.repository.reactive.ReactiveSortingRepository
相应选择命令式和响应式。
Choose imperative and reactive accordingly.
虽然在技术上没有禁止,但建议不要在同一应用程序中混合命令式和反应式数据库访问。我们不会对这样的场景提供支持。
While technically not prohibited, it is not recommended mixing imperative and reactive database access in the same application. We won’t support you with scenarios like this.
另一个选择是建立在特定于存储的实现上,并获得开箱即用的所有支持方法。这种方法的优点也是其最大的缺点:一旦发布,所有这些方法都将成为您 API 的一部分。大多数情况下,删除一些东西比事后添加更困难。此外,使用特定于存储的内容会将您的存储泄露到域中。从性能的角度来看,没有损失。
The other option is to settle on a store specific implementation and gain all the methods we support out of the box. The advantage of this approach is also its biggest disadvantage: once out, all those methods will be part of your API. Most of the time it’s harder to take something away, than to add stuff afterwards. Furthermore, using store specifics leaks your store into your domain. From a performance point of view, there is no penalty.
适用于上述任何影片实体的响应式存储库如下所示:
A reactive repository fitting to any of the movie entities above looks like this:
Unresolved include directive in modules/ROOT/pages/getting-started.adoc - include::example$documentation/domain/MovieRepository.java[]
使用 |
Testing reactive code is done with a |