开篇词

该指南将引导你完成使用 Spring Data Neo4j 来构建应用,该应用将数据存储在基于图形的数据库 Neo4j 中并从中检索数据。  

你将创建的应用

我们将使用 Neo4j 的基于 NoSQL 图数据存储来构建嵌入式 Neo4j 服务器,存储实体和关系以及查询的开发。  

你将需要的工具

  • 大概 15 分钟左右;
  • 你最喜欢的文本编辑器或集成开发环境(IDE)
  • JDK 1.8 或更高版本;
  • Gradle 4+ 或 Maven 3.2+
  • 你还可以将代码直接导入到 IDE 中:
  • Spring Too Suite (STS)
  • IntelliJ IDEA  

如何完成这个指南

像大多数的 Spring 入门指南一样,你可以从头开始并完成每个步骤,也可以绕过你已经熟悉的基本设置步骤。如论哪种方式,你最终都有可以工作的代码。

  • 要从头开始,移步至从 Spring Initializr 开始;
  • 要跳过基础,执行以下操作:
  • 下载并解压缩该指南将用到的源代码,或借助 Git 来对其进行克隆操作:git clone https://github.com/spring-guides/gs-accessing-data-neo4j.git
  • 切换至 gs-accessing-data-neo4j/initial 目录;
  • 跳转至该指南的定义一个简单的实体。

待一切就绪后,可以检查一下 gs-accessing-data-neo4j/complete 目录中的代码。  

从 Spring Initializr 开始

对于所有的 Spring 应用来说,你应该从 Spring Initializr 开始。Initializr 提供了一种快速的方法来提取应用程序所需的依赖,并为你完成许多设置。该例子只需要 Spring Data Neo 4j 启动器。下图显示了此示例项目的 Initializr 设置:

springboot连接neo4j配置文件 springboot neo4j查询_Neo4j图数据库

上图显示了选择 Maven 作为构建工具的 Initializr。你也可以使用 Gradle。它还将 com.example 和 accessing-data-neo4j 的值分别显示为 Group 和 Artifact。在本示例的其余部分,将用到这些值。

以下清单显示了选择 Maven 时创建的 pom.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>accessing-data-neo4j</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>accessing-data-neo4j</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

以下清单显示了在选择 Gradle 时创建的 build.gradle 文件:

plugins {
	id 'org.springframework.boot' version '2.2.2.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

 

搭建 Neo4j 服务器

在构建此应用之前,我们需要安装 Neo4j 服务器。

Neo4j 有一个开源服务器,我们可以免费安装。

在安装了 Homebrew 的 Mac 上,运行以下命令:

$ brew install neo4j

有关其他选项,请访问 https://neo4j.com/download/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/3.0.6/libexec/logs/neo4j.log for current status.

默认情况下,Neo4j 的用户名和密码为 neo4j 和 neo4j。但是,它要求更改至新的账户密码。为此,请运行以下命令:

curl -v -u neo4j:neo4j POST localhost:7474/user/neo4j/password -H "Content-type:application/json" -d "{\"password\":\"secret\"}"

这会将密码从 neo4j 更改为 secret,不要再生产中这么做!完成该步骤后,我们应该准备运行该指南的其余部分。  

定义一个简单的实体

Neo4j 捕获实体及其关系,这两个方面都具有同等重要的意义。想象一下,我们正在建立一个系统,在其中存储每个人的记录。但是,我们还想跟踪一个人的同事(在该示例中为 teammates)。使用 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.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;

@NodeEntity
public class Person {

  @Id @GeneratedValue private Long id;

  private String name;

  private Person() {
    // Empty constructor required as of Neo4j API 2.0.5
  };

  public Person(String name) {
    this.name = name;
  }

  /**
   * Neo4j doesn't REALLY have bi-directional relationships. It just means when querying
   * to ignore the direction of the relationship.
   * https://dzone.com/articles/modelling-data-neo4j
   */
  @Relationship(type = "TEAMMATE", direction = Relationship.UNDIRECTED)
  public Set<Person> teammates;

  public void worksWith(Person person) {
    if (teammates == null) {
      teammates = new HashSet<>();
    }
    teammates.add(person);
  }

  public String toString() {

    return this.name + "'s teammates => "
      + Optional.ofNullable(this.teammates).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。

Person 类使用 @NodeEntity 注解。Neo4j 存储它时,将创建一个新节点。该类还具有一个标注为 @GraphId 的 id。Neo4j 内部使用 @GraphId 来跟踪数据。

下一个重要的不问是 teammates。它是一个简单的 Set\<Person\> 类型,但标注为 @Relationship。这意味着该集合的每个成员也应作为单独的 Person 节点存在。请注意如何将方向设置为 UNDIRECTED。这意味着当我们查询 TEAMMATE 关系时,Spring Date Neo4j 会忽略关系的方向。

使用 worksWith() 方法,我们可以轻松地将人们联系在一起。

最后,我们有一个方便的 toString() 方法来打印此人的姓名和同事。  

创建简单查询

Spring Data Neo4j 专注于在 Neo4j 中存储数据。但它继承了 Spring Data Commons 项目的功能,包括导出查询的功能。本质上,我们无需学习 Neo4j 的查询语言。相反,我们可以编写一些方法并让查询为我们继续编写。

若要查看其工作原理,请创建一个查询 Person 节点的接口。以下清单(在 src/main/java/com/example/accessingdataneo4j/PersonRepository.java 中)显示了该查询:

package com.example.accessingdataneo4j;

import org.springframework.data.repository.CrudRepository;

public interface PersonRepository extends CrudRepository<Person, Long> {

	Person findByName(String name);
}

PersonRepository 扩展了 GraphRepository 接口,并插入其操作的类型:Person。该接口包含许多操作,包括标准的 CRUD(创建、读取、更新和删除)操作。

但我们可以通过声明其他查询的方法签名来定义它们。在这种情况下,我们添加了 findByName,它查找类行为 Perosn 的节点并找到与 name 匹配的节点。我们还可以使用 findByTeammatesName 来查找 Person 节点,并深入 teammates 字段的每个条目,并根据队友的 name 进行匹配。  

访问 Neo4j 的权限

Neo4j 社区版本需要凭据才能访问它。我们可以通过设置几个属性(在 src/main/resources/application.properties 中)来配置这些凭据,如下清单所示:

spring.data.neo4j.username=neo4j
spring.data.neo4j.password=secret

其中包括默认用户名(neo4j)和我们之前新设置的密码(secret)。

不要在源仓库中存储真实凭证。而是在我们的运行时中使用 SpringBoot 属性替换。

有了这个,我们可以将其连接起来,看看它是什么样子!  

创建应用类

Spring Initializr 为应用创建一个简单的类。以下清单显示了 Initializr 为该示例创建的类(在 src/main/java/com/example/accessingdatamysql/AccessingDataMysqlApplication.java 中):

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);
	}

	@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.worksWith(roy);
			greg.worksWith(craig);
			personRepository.save(greg);

			roy = personRepository.findByName(roy.getName());
			roy.worksWith(craig);
			// We already know that roy works with greg
			personRepository.save(roy);

			// We already know craig works with roy and greg

			log.info("Lookup each person by name...");
			team.stream().forEach(person -> log.info(
					"\t" + personRepository.findByName(person.getName()).toString()));
		};
	}

}

@SpringBootApplication 是一个便利的注解,它添加了以下所有内容:

  • @Configuration:将类标记为应用上下文 Bean 定义的源;
  • @EnableAutoConfiguration:告诉 Spring Boot 根据类路径配置、其他 bean 以及各种属性的配置来添加 bean。
  • @ComponentScan:告知 Spring 在 com/example 包中寻找他组件、配置以及服务。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法启动应用。

只要它们包含在 @SpringBootApplication 类的同一包(或子包)中,Spring Boot 就会自动处理这些存储库。为了更好地控制注册过程,可以使用 @EnableNeo4jRepositories 来标注。

默认情况下,@EnableNeo4jRepositories 会在当前程序包中扫描任何扩展了 Spring Data 存储库接口之一的接口。如果我们的项目布局中有多个项目并且找不到存储库,则可以使用其 basePackageClasses=MyRepository.class 安全地告诉 Spring Data Neo4j 按类型扫描不同的根包。

显示日志记录输出。该服务应在几秒钟内启动并运行。

现在,自动装配我们先前定义的 PersonRepository 实例。Spring Data Neo4j 动态地实现了该接口,并插入了所需的查询代码以满足接口的职责。

main 方法使用 Spring Boot 的 SpringApplication.run() 启动应用并调用建立关系的 CommandLineRunner。

在这汇总情况下,我们将创建三个本地 Person 实例:Greg、Roy 和 Craig。最初,它们仅存在于内存中。请注意,还没有人是任何人的队友。

首先,我们会找到 Greg,指出他与 Roy 以及 Craig 合作,然后再次坚持下去。记住,队友关系被标记为 UNDIRECTED(即双向)。这意味着 Roy 和 Craig 也已更新。

这就是当我们需要更新 Roy 时的原因。首先从 Neo4j 获取该记录非常重要。在将 Craig 添加到列表之前,我们需要 Roy 队友的最新状态。

为什么没有代码可以获取 Craig 并添加任何关系?因为我们已经拥有它!Greg 先前将 Craig 标记为队友,Roy 也是如此。这意味着无需再次更新 Craig 的关系。遍历每个团队成员并将其信息打印到控制台时,我们可以看到它。

最后,查看其他查询中我们向后看的地方,回答 “谁与谁一起工作?” 的问题。  

构建可执行 JAR

我们可以结合 Gradle 或 Maven 来从命令行运行该应用。我们还可以构建一个包含所有必须依赖项、类以及资源的可执行 JAR 文件,然后运行该文件。在整个开发生命周期中,跨环境等等情况下,构建可执行 JAR 可以轻松地将服务作为应用进行发布、版本化以及部署。

如果使用 Gradle,则可以借助 ./gradlew bootRun 来运行应用。或通过借助 ./gradlew build 来构建 JAR 文件,然后运行 JAR 文件,如下所示:

java -jar build/libs/gs-accessing-data-neo4j-0.1.0.jar

由官网提供的以上这条命令的执行结果与我本地的不一样,我需要这样才能运行:java -jar build/libs/accessing-data-neo4j-0.0.1-SNAPSHOT.jar。

如果使用 Maven,则可以借助 ./mvnw spring-boot:run 来运行该用。或可以借助 ./mvnw clean package 来构建 JAR 文件,然后运行 JAR 文件,如下所示:

java -jar target/gs-accessing-data-neo4j-0.1.0.jar

由官网提供的以上这条命令的执行结果与我本地的不一样,我需要这样才能运行:java -jar target/accessing-data-neo4j-0.0.1-SNAPSHOT.jar。

我们还可以构建一个经典的 WAR 文件。

我们应该看到以下输出(以及其他内容,例如查询):

Before linking up with Neo4j...
	Greg's teammates => []
	Roy's teammates => []
	Craig's teammates => []

Lookup each person by name...
	Greg's teammates => [Roy, Craig]
	Roy's teammates => [Greg, Craig]
	Craig's teammates => [Roy, Greg]

从输出中可以看到,(最初)没有任何人通过任何关系被连接。然后,在添加人员后,他们就被绑定在一起了。最后,我们可以看到根据队友查找人的便捷查询。  

概述

恭喜你!我们只需要设置一个嵌入式 Neo4j 服务器,存储一些简单的相关实体,并开发一些快速查询。

如果要毫不费力地使用基于超媒体的 RESTful 前端公开 Neo4j 存储库,请阅读使用 REST 访问 Neo4j 数据(尽请期待~)。  

参见

以下指南也可能会有所帮助:

  • 使用 REST 访问 Neo4j 数据(尽请期待~)
  • 使用 MySQL 访问数据(尽请期待~)
  • 使用 JPA 访问数据(尽请期待~)
  • 使用 MongoDB 访问数据(尽请期待~)
  • 使用 Gemfire 访问数据(尽请期待~)

想看指南的其他内容?请访问该指南的所属专栏:《Spring 官方指南》