開篇詞
該指南將引導你創建通過基於超媒體的 RESTful 前端訪問圖數據。
你將創建的應用
我們將構建一個 Spring 應用,該應用允許我們使用 Spring Data REST 創建和檢索存儲在 Neo4j NoSQL 數據庫中的 Person
對象。Spring Data REST 具有 Spring HATEOAS 和 Spring Data Neo4j 的功能,並將它們自動結合在一起。
你將需要的工具
- 大概 15 分鐘左右;
- 你最喜歡的文本編輯器或集成開發環境(IDE)
- JDK 1.8 或更高版本;
- Gradle 4+ 或 Maven 3.2+
- 你還可以將代碼直接導入到 IDE 中:
如何完成這個指南
像大多數的 Spring 入門指南一樣,你可以從頭開始並完成每個步驟,也可以繞過你已經熟悉的基本設置步驟。如論哪種方式,你最終都有可以工作的代碼。
- 要從頭開始,移步至搭建並啓動一個 Neo4j 服務器;
- 要跳過基礎,執行以下操作:
- 下載並解壓縮該指南將用到的源代碼,或藉助 Git 來對其進行克隆操作:
git clone https://github.com/spring-guides/gs-accessing-neo4j-data-rest.git
- 切換至
gs-accessing-neo4j-data-rest/initial
目錄; - 跳轉至該指南的訪問 Neo 4j 的權限。
- 下載並解壓縮該指南將用到的源代碼,或藉助 Git 來對其進行克隆操作:
待一切就緒後,可以檢查一下 gs-accessing-neo4j-data-rest/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 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 -X POST localhost:7474/user/neo4j/password -H "Content-type:application/json" -d "{\"password\":\"secret\"}"
這會將密碼從 neo4j
更改爲 secret
(不要在生產環境中這麼做!)。完成後,我們就可以準備運行該指南。
從 Spring Initializr 開始
對於所有的 Spring 應用來說,你應該從 Spring Initializr 開始。Initializr 提供了一種快速的方法來提取應用程序所需的依賴,併爲你完成許多設置。該例子需要 Rest Repositories 和 Spring Data Neo4j 依賴。下圖顯示了此示例項目的 Initializr 設置:
上圖顯示了選擇 Maven 作爲構建工具的 Initializr。你也可以使用 Gradle。它還將
com.example
和accessing-neo4j-data-rest
的值分別顯示爲 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-neo4j-data-rest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>accessing-neo4j-data-rest</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-data-rest</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'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
訪問 Neo 4j 的權限
Neo4j Community Edition 需要憑據才能訪問它。我們可以通過在 src/main/resources/application.properties
中設置屬性來配置憑據,如下所示:
spring.data.neo4j.username=neo4j
spring.data.neo4j.password=secret
這包括默認的用戶名(neo4j
)和我們先前設置的新密碼(secret
)。
不要在源存儲庫中存儲真實憑證。相反,請使用 Spring Boot 的屬性替代在運行中對它們進行配置。
創建域對象
我們需要創建一個新的域對象來展現一個人,如以下示例(在 src/main/java/com/example/accessingneo4jdatarest/Person.java
中)所示:
package com.example.accessingneo4jdatarest;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
@NodeEntity
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.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findByLastName(@Param("name") String name);
}
該存儲庫是一個接口,可讓我們執行涉及 Person
對象的各種操作。它通過擴展 Spring Data Commons 中定義的 PagingAndSortingRepository
接口來獲得這些操作。
在運行時,Spring Data REST 自動創建該接口的實現。然後,它使用 @RepositoryRestResources
註解指示 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。@ComponentScan
:告知 Spring 在com/example
包中尋找他組件、配置以及服務。
main()
方法使用 Spring Boot 的 SpringApplication.run()
方法啓動應用。
@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/gs-accessing-neo4j-data-rest-0.1.0.jar
由官網提供的以上這條命令的執行結果與我本地的不一樣,我需要這樣才能運行:
java -jar build/libs/accessing-neo4j-data-rest-0.0.1-SNAPSHOT.jar
。
如果使用 Maven,則可以藉助 ./mvnw spring-boot:run
來運行該用。或可以藉助 ./mvnw clean package
來構建 JAR 文件,然後運行 JAR 文件,如下所示:
java -jar target/gs-accessing-neo4j-data-rest-0.1.0.jar
由官網提供的以上這條命令的執行結果與我本地的不一樣,我需要這樣才能運行:
java -jar target/accessing-neo4j-data-rest-0.0.1-SNAPSHOT.jar
。
我們還可以將 JAR 應用轉換成 WAR 應用。
顯示日誌記錄輸出。該服務應在幾秒內啓動並運行。
測試應用
現在該應用正在運行,我們可以對其進行測試。我們可以使用任何喜歡的 REST 客戶端。以下示例使用 curl
。
首先,我們要查看頂級服務。以下示例(帶有輸出)顯示瞭如何執行該操作:
curl http://localhost:8080
{
"_links" : {
"people" : {
"href" : "http://localhost:8080/people{?page,size,sort}",
"templated" : true
}
}
}
這裏,我們可以初步瞭解該服務器所提供的功能。在 http://localhost:8080/people 上有一個 people 鏈接。它有一些選項,例如 ?page
、?size
及 ?sort
。
Spring Data REST 使用 HAL 格式進行 JSON 輸出。它非常靈活,並提供了一種便捷的方式來提供與所提供數據相鄰的鏈接。
curl http://localhost:8080/people
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/people{?page,size,sort}",
"templated" : true
},
"search" : {
"href" : "http://localhost: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" }' http://localhost:8080/people
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Location: http://localhost: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": "Barggins""}'
:被髮送的數據;
請注意,對
POST
操作的響應如何包含Location
標頭。它包含新創建資源的 URI。Spring Data REST 還具有兩個方法(RepositoryRestConfiguration.setReturnBodyOnCreate(…)
與setReturnBodyOnUpdate(…)
),我們可以使用它們來配置框架以立即返回剛剛創建的資源的表示形式。
RepositoryRestConfiguration.setReturnBodyForPutAndPost(…)
是一種啓用創建和更新操作的表示形式響應的快捷方式。
我們可以查詢所有人,如以下示例所示:
curl http://localhost:8080/people
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/people{?page,size,sort}",
"templated" : true
},
"search" : {
"href" : "http://localhost:8080/people/search"
}
},
"_embedded" : {
"people" : [ {
"firstName" : "Frodo",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/0"
}
}
} ]
},
"page" : {
"size" : 20,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
people
對象包含一個包含 Frodo
的列表。注意它是如何包含一個 self
鏈接的。Spring Data REST 還使用 Evo Inflector 來對實體名稱進行復數以進行分組。
我們可以直接查詢單個記錄,如下所示:
curl http://localhost:8080/people/1
{
"firstName" : "Frodo",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/0"
}
}
}
這似乎完全是基於 Web 的,但是後臺有一個嵌入式 Neo4j 圖形數據庫。在生產中,我們可能會連接到獨立的 Neo4j 服務器。
在該指南中,只有一個域對象。在域對象相互關聯的更復雜的系統中,Spring Data REST 展示了更多鏈接,以幫助導航至連接的記錄。
我們可以通過運行以下命令(及其輸出顯示)找到所有自定義查詢:
curl http://localhost:8080/people/search
{
"_links" : {
"findByLastName" : {
"href" : "http://localhost:8080/people/search/findByLastName{?name}",
"templated" : true
}
}
}
我們可以看到查詢的 URL,包括 HTTP 查詢參數,name
。請注意,這與接口中嵌入的 @Param("name")
註解匹配。
以下示例顯示瞭如何使用 findByLastName
查詢:
curl http://localhost:8080/people/search/findByLastName?name=Baggins
{
"_embedded" : {
"people" : [ {
"firstName" : "Frodo",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/0"
},
"person" : {
"href" : "http://localhost:8080/people/0"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/search/findByLastName?name=Baggins"
}
}
}
因爲我們已將其定義爲在代碼中返回 List<Person>
,所以它將返回所有結果。如果已將其定義爲僅返回 Person
,則它將選擇要返回的 Person
對象之一。由於這可能是不可預測的,因此對於可能返回多個條目的查詢,我們可能不想這樣做。
我還可以發出 PUT
、PATCH
和 DELETE
REST 調用來分別替換、更新或刪除現有記錄。以下示例使用 PUT
調用:
curl -X PUT -H "Content-Type:application/json" -d '{ "firstName": "Bilbo", "lastName": "Baggins" }' http://localhost:8080/people/0
curl http://localhost:8080/people/0
{
"firstName" : "Bilbo",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/0"
}
}
}
下面示例使用 PATCH
調用:
curl -X PATCH -H "Content-Type:application/json" -d '{ "firstName": "Bilbo Jr." }' http://localhost:8080/people/0
curl http://localhost:8080/people/0
{
"firstName" : "Bilbo Jr.",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/0"
}
}
}
PUT
替換整個記錄。未提供的字段將替換爲null
。我們可以使用PATCH
更新項的子集。
我們還可以刪除記錄,如以下示例所示:
curl -X DELETE http://localhost:8080/people/0
curl http://localhost:8080/people
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/people{?page,size,sort}",
"templated" : true
},
"search" : {
"href" : "http://localhost:8080/people/search"
}
},
"page" : {
"size" : 20,
"totalElements" : 0,
"totalPages" : 0,
"number" : 0
}
}
該超媒體驅動接口的一個方便方面是,我們可以使用 curl(或您喜歡的任何REST客戶端)來
發現所有 RESTful 端點。我們無需與客戶交換正式合同或接口文件。
概述
恭喜你!我們已經開發了具有基於超媒體的 RESTful 前端和基於 Neo4j 後端的應用。
參見
以下指南也可能會有所幫助:
- 使用 REST 訪問 JPA 數據
- 使用 REST 訪問 MongoDB 數據(盡請期待~)
- 使用 MySQL 訪問數據(盡請期待~)
- 使用 REST 訪問 GemFire 數據(盡請期待~)
- 消費 RESTful Web 服務
- 使用 AngularJS 消費 RESTful Web 服務
- 使用 jQuery 消費 RESTful Web 服務
- 使用 rest.js 消費 RESTful Web 服務
- 保護 Web 應用程序
- 使用 Spring 構建 REST 服務
- 使用 Spring Boot 構建應用程序
- 使用 Restdocs 創建 API 文檔(盡請期待~)
- 爲 RESTful Web 服務啓用跨源請求(盡請期待~)
- 構建超媒體驅動的 RESTful Web 服務
想看指南的其他內容?請訪問該指南的所屬專欄:《Spring 官方指南》