SpringBoot 2 REST 訪問 JPA 數據

開篇詞

該指南將引導你通過基於超媒體RESTful 前端訪問關係型 JPA 數據的應用。
 

你將創建的應用

我們將構建一個 Spring 應用,該應用允許我們使用 Spring Data REST 創建和檢索存儲在數據庫中的 Person 對象。Spring Data REST 具有 Spring HATEOASSpring Data JPA 的功能,並將它們自動結合在一起。

Spring Data REST 還支持將 Spring Dtaa Neo4j(盡請期待~)、Spring Data Gemfire(盡請期待~) 及 Spring Data MongoDB(盡請期待~) 作爲後端數據存儲,但是這些都不屬於該指南的一部分。

 

你將需要的工具

如何完成這個指南

像大多數的 Spring 入門指南一樣,你可以從頭開始並完成每個步驟,也可以繞過你已經熟悉的基本設置步驟。如論哪種方式,你最終都有可以工作的代碼。

  • 要從頭開始,移步至從 Spring Initializr 開始
  • 要跳過基礎,執行以下操作:
    • 下載並解壓縮該指南將用到的源代碼,或藉助 Git 來對其進行克隆操作:git clone https://github.com/spring-guides/gs-accessing-data-rest.git
    • 切換至 gs-accessing-data-rest/initial 目錄;
    • 跳轉至該指南的創建域對象

待一切就緒後,可以檢查一下 gs-accessing-data-rest/complete 目錄中的代碼。
 

從 Spring Initializr 開始

對於所有的 Spring 應用來說,你應該從 Spring Initializr 開始。Initializr 提供了一種快速的方法來提取應用程序所需的依賴,併爲你完成許多設置。該示例需要 Rest Repositories,Spring Data JPA 和 H2 依賴。下圖顯示了此示例項目的 Initializr 設置:
Spring Initializr 界面

上圖顯示了選擇 Maven 作爲構建工具的 Initializr。你也可以使用 Gradle。它還將 com.exampleaccessing-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 對象之一。由於這可能是不可預測的,因此對於可能返回多個條目的查詢,我們可能不想這樣做。

我還可以發出 PUTPATCHDELETE 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 後端的應用。
 

參見

以下指南也可能會有所幫助:

想看指南的其他內容?請訪問該指南的所屬專欄:《Spring 官方指南

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章