通过 REST 访问 Neo4j 数据

本指南将指导你完成创建应用程序的过程,该应用程序通过基于 超媒体 的 RESTful 前端访问基于图的数据。

你将构建什么

你将构建一个 Spring 应用程序,该应用程序允许你创建和检索存储在 Neo4j NoSQL 数据库中的 Person 对象,方法是使用 Spring Data REST。Spring Data REST 采用了 Spring HATEOASSpring Data Neo4j 的特性,并自动将它们组合在一起。

Spring Data REST 还支持 Spring Data JPASpring Data GemfireSpring Data MongoDB 作为后端数据存储,但本指南涉及 Neo4j。

你需要什么

如何完成本指南

与大多数 Spring 入门指南 一样,你可以从头开始并完成每一步,或者你可以跳过你已经熟悉的设置步骤。无论哪种方式,你最终都会得到可用的代码。

从头开始,请转到 [scratch]

跳过基础知识,请执行以下操作

完成后,可以根据 {project_id}/complete 中的代码检查结果。

启动 Neo4j 服务器

在构建此应用程序之前,需要设置 Neo4j 服务器。

Neo4j 有一个可免费安装的开源服务器。

在安装了 Homebrew 的 Mac 上,可以在终端窗口中键入以下内容

$ brew install neo4j

有关其他选项,请参阅 https://neo4j.com/download/community-edition/

安装 Neo4j 后,可以通过运行以下命令使用其默认设置启动它

$ neo4j start

你应该会看到类似于以下内容的消息

Starting Neo4j.
Started neo4j (pid 96416). By default, it is available at https://127.0.0.1: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 的用户名和密码为 neo4jneo4j。但是,它要求更改新帐户密码。为此,请运行以下命令

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

这会将密码从 neo4j 更改为 secret(在生产中不要这样做!)。完成后,你应该已准备好运行本指南。

从 Spring Initializr 开始

可以使用此 预初始化项目 并单击“生成”以下载 ZIP 文件。此项目已配置为适合本教程中的示例。

手动初始化项目

  1. 导航至 https://start.spring.io。此服务会提取应用程序所需的所有依赖项,并为你完成大部分设置。

  2. 选择 Gradle 或 Maven 以及要使用的语言。本指南假设你选择了 Java。

  3. 单击依赖项并选择Rest 存储库Spring Data Neo4j

  4. 单击生成

  5. 下载生成的 ZIP 文件,它是一个已根据你的选择配置的 Web 应用程序的存档。

如果你的 IDE 集成了 Spring Initializr,则可以从 IDE 完成此过程。
你还可以从 Github 分叉项目并在你的 IDE 或其他编辑器中打开它。

访问 Neo4j 的权限

Neo4j 社区版需要凭据才能访问它。你可以通过在 src/main/resources/application.properties 中设置属性来配置凭据,如下所示

spring.neo4j.uri=bolt://127.0.0.1:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=secret

这包括默认用户名 (neo4j) 和你之前设置的新密码 (secret)。

不要将实际凭据存储在源代码仓库中。相反,使用 Spring Boot 的属性覆盖 在运行时配置它们。

创建域对象

你需要创建一个新的域对象来表示一个人,如下例(在 src/main/java/com/example/accessingneo4jdatarest/Person.java)所示

package com.example.accessingneo4jdatarest;

import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.GeneratedValue;

@Node
public class Person {

  @Id @GeneratedValue private Long id;

  private String firstName;
  private String lastName;

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

Person 对象有一个名字和一个姓氏。还有一个 ID 对象被配置为自动生成,这样你就不需要自己去做了。

创建 Person 存储库

接下来,你需要创建一个简单的存储库,如下例(在 src/main/java/com/example/accessingneo4jdatarest/PersonRepository.java)所示

package com.example.accessingneo4jdatarest;

import java.util.List;

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long>, CrudRepository<Person, Long> {

  List<Person> findByLastName(@Param("name") String name);

}

这个存储库是一个接口,它允许你执行涉及 Person 对象的各种操作。它通过扩展 Spring Data Commons 中定义的 PagingAndSortingRepositry 接口来获取这些操作。

在运行时,Spring Data REST 会自动创建这个接口的一个实现。然后它使用 @RepositoryRestResource 注解来指示 Spring MVC 在 /people 创建 RESTful 端点。

@RepositoryRestResource 不是存储库导出的必需条件。它仅用于更改导出详细信息,例如使用 /people 而不是默认值 /persons

这里你还定义了一个自定义查询,以基于 lastName 值检索 Person 对象的列表。你可以在本指南的后面看到如何调用它。

查找应用程序类

当你使用 Spring Initializr 创建项目时,它会创建一个应用程序类。你可以在 src/main/java/com/example/accessingneo4jdatarest/Application.java 中找到它。请注意,Spring Initializr 会连接(并正确更改大小写)包名,并将其添加到 Application 以创建应用程序类名。在这种情况下,我们得到了 AccessingNeo4jDataRestApplication,如下表所示

package com.example.accessingneo4jdatarest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement
@EnableNeo4jRepositories
@SpringBootApplication
public class AccessingNeo4jDataRestApplication {

  public static void main(String[] args) {
    SpringApplication.run(AccessingNeo4jDataRestApplication.class, args);
  }
}

对于此示例,你不需要对这个应用程序类进行任何更改

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

  • @Configuration:将类标记为应用程序上下文的 bean 定义的来源。

  • @EnableAutoConfiguration:指示 Spring Boot 根据类路径设置、其他 Bean 和各种属性设置开始添加 Bean。例如,如果类路径上存在 spring-webmvc,此注释将应用程序标记为 Web 应用程序并激活关键行为,例如设置 DispatcherServlet

  • @ComponentScan:指示 Spring 在 com/example 包中查找其他组件、配置和服务,以便它找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法启动应用程序。您是否注意到没有一行 XML?也没有 web.xml 文件。此 Web 应用程序 100% 是纯 Java,您不必处理配置任何管道或基础设施。

@EnableNeo4jRepositories 注释激活 Spring Data Neo4j。Spring Data Neo4j 创建 PersonRepository 的具体实现,并通过使用 Cypher 查询语言将其配置为与嵌入式 Neo4j 数据库通信。

构建可执行 JAR

您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必需的依赖项、类和资源的可执行 JAR 文件并运行它。构建可执行 jar 可以轻松地在整个开发生命周期、不同环境中对服务进行打包、版本化和部署,等等。

如果您使用 Gradle,可以使用 ./gradlew bootRun 运行应用程序。或者,您可以使用 ./gradlew build 构建 JAR 文件,然后运行 JAR 文件,如下所示

java -jar build/libs/{project_id}-0.1.0.jar

如果您使用 Maven,可以使用 ./mvnw spring-boot:run 运行应用程序。或者,您可以使用 ./mvnw clean package 构建 JAR 文件,然后运行 JAR 文件,如下所示

java -jar target/{project_id}-0.1.0.jar
此处描述的步骤创建了一个可运行的 JAR。您还可以 构建一个经典 WAR 文件

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

测试应用程序

现在应用程序正在运行,您可以对其进行测试。您可以使用任何您希望的 REST 客户端。以下示例使用名为 curl 的 *nix 工具。

首先,您希望看到顶级服务。以下示例(带输出)展示了如何执行此操作

$ curl https://127.0.0.1:8080
{
  "_links" : {
    "people" : {
      "href" : "https://127.0.0.1:8080/people{?page,size,sort}",
      "templated" : true
    }
  }
}

在这里,您可以初步了解此服务器提供的功能。有一个位于 https://127.0.0.1:8080/peoplepeople 链接。它有一些选项,例如 ?page?size?sort

Spring Data REST 使用 HAL 格式 作为 JSON 输出。它很灵活,并提供了一种方便的方式来提供与所提供数据相邻的链接。
$ curl https://127.0.0.1:8080/people
{
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "https://127.0.0.1:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

目前没有元素,因此也没有页面,所以现在是时候创建一个新的 Person!为此,运行以下命令(显示其输出)

$ curl -i -X POST -H "Content-Type:application/json" -d '{  "firstName" : "Frodo",  "lastName" : "Baggins" }' https://127.0.0.1:8080/people
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Location: https://127.0.0.1:8080/people/0
Content-Length: 0
Date: Wed, 26 Feb 2014 20:26:55 GMT
  • -i 确保您可以看到包括标头在内的响应消息。显示了新创建的 Person 的 URI

  • -X POST 表示这是一个用于创建新条目的 POST

  • -H "Content-Type:application/json" 设置内容类型,以便应用程序知道有效负载包含一个 JSON 对象

  • -d '{ "firstName" : "Frodo", "lastName" : "Baggins" }' 是正在发送的数据

请注意,先前的 POST 操作如何包含一个 Location 标头。其中包含新创建的资源的 URI。Spring Data REST 还有两种方法(RepositoryRestConfiguration.setReturnBodyOnCreate(…)setReturnBodyOnCreate(…)),您可以使用它们来配置框架以立即返回刚刚创建的资源的表示形式。

由此,您可以通过运行以下命令(显示其输出)来查询所有人员

$ curl https://127.0.0.1:8080/people
{
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "https://127.0.0.1:8080/people/search"
    }
  },
  "_embedded" : {
    "people" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "https://127.0.0.1:8080/people/0"
        }
      }
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

people 对象包含一个带有 Frodo 的列表。请注意,它如何包含一个 self 链接。Spring Data REST 还使用 Evo Inflector 库来使实体的名称复数化以进行分组。

您可以通过运行以下命令(显示其输出)直接查询单个记录

$ curl https://127.0.0.1:8080/people/0
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/0"
    }
  }
}
这似乎纯粹基于 Web,但是,在幕后,有一个嵌入式 Neo4j 图形数据库。在生产中,您可能会连接到一个独立的 Neo4j 服务器。

在本指南中,只有一个域对象。对于一个更复杂的系统,其中域对象相互关联,Spring Data REST 会呈现其他链接以帮助导航到连接的记录。

您可以通过运行以下命令(显示其输出)来查找所有自定义查询

$ curl https://127.0.0.1:8080/people/search
{
  "_links" : {
    "findByLastName" : {
      "href" : "https://127.0.0.1:8080/people/search/findByLastName{?name}",
      "templated" : true
    }
  }
}

您可以看到查询的 URL,包括 HTTP 查询参数:name。请注意,这与嵌入在接口中的 @Param("name") 注解相匹配。

要使用 findByLastName 查询,请运行以下命令(显示其输出)

$ curl https://127.0.0.1:8080/people/search/findByLastName?name=Baggins
{
  "_embedded" : {
    "people" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "https://127.0.0.1:8080/people/0"
        },
        "person" : {
          "href" : "https://127.0.0.1:8080/people/0"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/search/findByLastName?name=Baggins"
    }
  }
}

由于您在代码中定义了它返回 List<Person>,因此它返回所有结果。如果您已定义它仅返回 Person,它将选择一个 Person 对象返回。由于这可能是不可预测的,因此您可能不想对可以返回多个条目的查询执行此操作。

您还可以发出 PUTPATCHDELETE REST 调用来替换、更新或删除现有记录。以下示例(显示其输出)显示了一个 PUT 调用

$ curl -X PUT -H "Content-Type:application/json" -d '{ "firstName": "Bilbo", "lastName": "Baggins" }' https://127.0.0.1:8080/people/0
$ curl https://127.0.0.1:8080/people/0
{
  "firstName" : "Bilbo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/0"
    }
  }
}

以下示例(显示其输出)显示了一个 PATCH 调用

$ curl -X PATCH -H "Content-Type:application/json" -d '{ "firstName": "Bilbo Jr." }' https://127.0.0.1:8080/people/0
$ curl https://127.0.0.1:8080/people/0
{
  "firstName" : "Bilbo Jr.",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/0"
    }
  }
}
PUT 替换整个记录。未提供的字段将替换为 nullPATCH 可用于更新子集项目。

您还可以删除记录,如下例(显示其输出)所示

$ curl -X DELETE https://127.0.0.1:8080/people/0
$ curl https://127.0.0.1:8080/people
{
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "https://127.0.0.1:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

这种超媒体驱动的接口的一个便利方面是,您可以使用 curl(或您喜欢的任何 REST 客户端)发现所有 RESTful 端点。您无需与客户交换正式合同或接口文档。

摘要

恭喜!您刚刚开发了一个应用程序,其前端基于 超媒体 的 RESTful 且后端基于 Neo4j。

获取代码