開篇詞
該指南將引導你通過基於超媒體的 RESTful 前端訪問關係型 JPA 數據的應用。
你將創建的應用
我們將構建一個 Spring 應用,該應用允許我們使用 Spring Data REST 創建和檢索存儲在數據庫中的 Person
對象。Spring Data REST 具有 Spring HATEOAS 和 Spring Data JPA 的功能,並將它們自動結合在一起。
Spring Data REST 還支持將 Spring Dtaa Neo4j(盡請期待~)、Spring Data Gemfire(盡請期待~) 及 Spring Data MongoDB(盡請期待~) 作爲後端數據存儲,但是這些都不屬於該指南的一部分。
你將需要的工具
- 大概 15 分鐘左右;
- 你最喜歡的文本編輯器或集成開發環境(IDE)
- JDK 1.8 或更高版本;
- Gradle 4+ 或 Maven 3.2+
- 你還可以將代碼直接導入到 IDE 中:
如何完成這個指南
像大多數的 Spring 入門指南一樣,你可以從頭開始並完成每個步驟,也可以繞過你已經熟悉的基本設置步驟。如論哪種方式,你最終都有可以工作的代碼。
- 要從頭開始,移步至從 Spring Initializr 開始;
- 要跳過基礎,執行以下操作:
待一切就緒後,可以檢查一下 gs-accessing-data-rest/complete
目錄中的代碼。
從 Spring Initializr 開始
對於所有的 Spring 應用來說,你應該從 Spring Initializr 開始。Initializr 提供了一種快速的方法來提取應用程序所需的依賴,併爲你完成許多設置。該示例需要 Rest Repositories,Spring Data JPA 和 H2 依賴。下圖顯示了此示例項目的 Initializr 設置:
上圖顯示了選擇 Maven 作爲構建工具的 Initializr。你也可以使用 Gradle。它還將
com.example
和accessing-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-data-rest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>accessing-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-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</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-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
創建域對象
創建一個新的域對象來展示一個人,如以下清單(在 src/main/java/com/example/accessingdatarest/Person.java
中)所示:
package com.example.accessingdatarest;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
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/accessingdatarest/PersonRepository.java
中)所示:
package com.example.accessingdatarest;
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 將自動創建該接口的實現。然後它將使用 @RepositoryRestResource 註解指示 Spring MVC 在 /people
處創建 RESTful 端點。
@RepositoryRestResource
不需要被存儲庫所導出。它僅用於更改導出詳情,例如使用/people
代替/persons
的默認值。
在這裏,我們還定義了一個自定義查詢,以基於 lastName
檢索 Person
對象的列表。我們可以在該指南後面部分中看到如何調用它。
@SpringBootApplication
是一個便利的註解,它添加了以下所有內容:
@Configuration
:將類標記爲應用上下文 Bean 定義的源;@EnableAutoConfiguration
:告訴 Spring Boot 根據類路徑配置、其他 bean 以及各種屬性的配置來添加 bean。@ComponentScan
:告知 Spring 在com/example
包中尋找他組件、配置以及服務。
main()
方法使用 Spring Boot 的 SpringApplication.run()
方法啓動應用。
Spring Boot 自動啓動 Spring Data JPA,以創建 PersonRepository
的具體實現,並將其配置爲使用 JPA 與後端內存數據庫進行對話。
Spring Data REST 建立在 Spring MVC 之上。它創建了 Spring MVC 控制器,JSON 轉換器和其他 bean 的集合,以提供 RESTful 前端。這些組件鏈接到 Spring Data JPA 後端。當我們使用 Spring Boot 時,這些都是自動配置的。如果我們想研究它是如何工作的,請查看 Spring Data REST 中的 RepositoryRestMvcConfiguration
。
構建可執行 JAR
我們可以結合 Gradle 或 Maven 來從命令行運行該應用。我們還可以構建一個包含所有必須依賴項、類以及資源的可執行 JAR 文件,然後運行該文件。在整個開發生命週期中,跨環境等等情況下,構建可執行 JAR 可以輕鬆地將服務作爲應用進行發佈、版本化以及部署。
如果使用 Gradle,則可以藉助 ./gradlew bootRun
來運行應用。或通過藉助 ./gradlew build
來構建 JAR 文件,然後運行 JAR 文件,如下所示:
java -jar build/libs/gs-accessing-data-rest-0.1.0.jar
由官網提供的以上這條命令的執行結果與我本地的不一樣,我需要這樣才能運行:
java -jar build/libs/accessing-data-rest-0.0.1-SNAPSHOT.jar
。
如果使用 Maven,則可以藉助 ./mvnw spring-boot:run
來運行該用。或可以藉助 ./mvnw clean package
來構建 JAR 文件,然後運行 JAR 文件,如下所示:
java -jar target/gs-accessing-data-rest-0.1.0.jar
由官網提供的以上這條命令的執行結果與我本地的不一樣,我需要這樣才能運行:
java -jar target/accessing-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 { "_embedded" : { "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 -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/1 Content-Length: 0 Date: Wed, 26 Feb 2014 20:26:55 GMT
-i
:確保我們可以看到包括標題的響應消息。顯示新創建的 Person 的 URI;-H "Content-Type:application/json"
:設置內容類型,以便應用知道有效負載包含 JSON 對象;-d'{"firstName: "Frodo", "lastName": "Barggins""}'
:正在發送數據;- 如果你是用的是 Windows,則上面的命令將在 WSL 運行。如果無法安裝 WSL,則可能需要用雙引號替換單引號,並轉義現有的雙引號,即
-d "{\"firstName\": \"Frodo\", \"lastName\": \"Baggins\"}"
。
請注意,對
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/1" } } } ] }, "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/1" } } }
這似乎純粹是基於網絡的。但是,在後臺有一個 H2 關係數據庫。在生產中,我們可能會使用真實的數據庫,例如 PostgreSQL。
在該指南中,只有一個域對象。在域對象相互關聯的更復雜的系統中,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" : { "persons" : [ { "firstName" : "Frodo", "lastName" : "Baggins", "_links" : { "self" : { "href" : "http://localhost:8080/people/1" } } } ] } }
因爲我們已將其定義爲在代碼中返回 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/1
curl http://localhost:8080/people/1 { "firstName" : "Bilbo", "lastName" : "Baggins", "_links" : { "self" : { "href" : "http://localhost:8080/people/1" } } }
下面示例使用 PATCH
調用:
curl -X PATCH -H "Content-Type:application/json" -d '{"firstName": "Bilbo Jr."}' http://localhost:8080/people/1
curl http://localhost:8080/people/1 { "firstName" : "Bilbo Jr.", "lastName" : "Baggins", "_links" : { "self" : { "href" : "http://localhost:8080/people/1" } } }
PUT
替換整個記錄。未提供的字段將替換爲null
。我們可以使用PATCH
更新項的子集。
我們還可以刪除記錄,如以下示例所示:
curl -X DELETE http://localhost:8080/people/1
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 前端和基於 JPA 後端的應用。
參見
以下指南也可能會有所幫助:
- 構建超媒體驅動的 RESTful Web 服務
- 使用 REST 訪問 GemFire 數據(盡請期待~)
- 使用 REST 訪問 MongoDB 數據(盡請期待~)
- 使用 MySQL 訪問數據(盡請期待~)
- 使用 REST 訪問 Neo4j 數據(盡請期待~)
- 消費 RESTful Web 服務
- 使用 AngularJS 消費 RESTful Web 服務
- 使用 jQuery 消費 RESTful Web 服務
- 使用 rest.js 消費 RESTful Web 服務
- 保護 Web 應用程序
- 使用 Spring 構建 REST 服務
- 使用 Spring Boot 構建應用程序
- 使用 Restdocs 創建 API 文檔(盡請期待~)
- 爲 RESTful Web 服務啓用跨源請求(盡請期待~)
想看指南的其他內容?請訪問該指南的所屬專欄:《Spring 官方指南》