使用 REST 存取 Pivotal GemFire 中的資料

本指南將引導您完成建立應用程式的流程,該應用程式透過基於超媒體的 RESTful 前端存取儲存在 Apache Geode 中的資料。

您將建置的內容

您將建置一個 Spring Web 應用程式,讓您可以使用 Spring Data REST 建立和檢索儲存在 Apache Geode 記憶體內資料網格 (IMDG) 中的 Person 物件。Spring Data REST 結合了 Spring HATEOASSpring Data for Apache Geode 的功能,並自動將它們結合在一起。

Spring Data REST 也支援 Spring Data JPASpring Data MongoDBSpring Data Neo4j 作為後端資料儲存,但這些不屬於本指南的一部分。
如需更全面了解 Apache Geode 概念以及從 Apache Geode 存取資料,請閱讀指南 使用 Apache Geode 存取資料

您需要的東西

如何完成本指南

與大多數 Spring 入門指南一樣,您可以從頭開始並完成每個步驟,或者您可以跳過您已熟悉的基礎設定步驟。無論哪種方式,您最終都會得到可運作的程式碼。

若要從頭開始,請繼續前往 從 Spring Initializr 開始

若要跳過基礎知識,請執行以下操作

當您完成時,您可以將您的結果與 gs-accessing-gemfire-data-rest/complete 中的程式碼進行比較。

從 Spring Initializr 開始

對於所有 Spring 應用程式,您應該從 Spring Initializr 開始。Spring Initializr 提供了一種快速的方法來提取應用程式所需的所有依賴項,並為您完成許多設定工作。此範例需要「Spring for Apache Geode」依賴項。

以下清單顯示了使用 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.7.0</version>
	</parent>

	<groupId>org.springframework</groupId>
	<artifactId>gs-accessing-gemfire-data-rest</artifactId>
	<version>0.1.0</version>

	<properties>
		<spring-shell.version>1.2.0.RELEASE</spring-shell.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-rest</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-geode</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.shell</groupId>
			<artifactId>spring-shell</artifactId>
			<version>${spring-shell.version}</version>
			<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.7.0'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'io.freefair.lombok' version '6.3.0'
    id 'java'
}

apply plugin: 'eclipse'
apply plugin: 'idea'

group = "org.springframework"
version = "0.1.0"
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {

    implementation "org.springframework.boot:spring-boot-starter-data-rest"
    implementation "org.springframework.data:spring-data-geode"
    implementation "org.projectlombok:lombok"

    runtimeOnly "org.springframework.shell:spring-shell:1.2.0.RELEASE"

    testImplementation "org.springframework.boot:spring-boot-starter-test"

}

test {
    useJUnitPlatform()
}

bootJar {
    baseName = 'gs-accessing-gemfire-data-rest'
    version =  '0.1.0'
}

建立網域物件

建立一個新的網域物件來表示一個人。

src/main/java/hello/Person.java

package hello;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.gemfire.mapping.annotation.Region;

import lombok.Data;

@Data
@Region("People")
public class Person {

  private static AtomicLong COUNTER = new AtomicLong(0L);

  @Id
  private Long id;

  private String firstName;
  private String lastName;

  @PersistenceConstructor
  public Person() {
    this.id = COUNTER.incrementAndGet();
  }
}

Person 具有名字和姓氏。Apache Geode 網域物件需要一個 id,因此使用 AtomicLong 在每次建立 Person 物件時遞增。

建立 Person Repository

接下來,您需要建立一個簡單的 Repository 來持久化/存取儲存在 Apache Geode 中的 Person 物件。

src/main/java/hello/PersonRepository.java

package hello;

import java.util.List;

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 CrudRepository<Person, Long> {

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

}

Repository 是一個介面,將允許您執行各種資料存取操作(例如,基本的 CRUD 和簡單查詢),涉及 Person 物件。它透過擴展 CrudRepository 來獲得這些操作。

在執行時期,Spring Data for Apache Geode 將自動建立此介面的實作。然後,Spring Data REST 將使用 @RepositoryRestResource 註解來指示 Spring MVC 在 /people 建立 RESTful 端點。

匯出 Repository 不需要 @RepositoryRestResource。它僅用於更改匯出詳細資訊,例如使用 /people 而不是預設值 /persons

在這裡,您也定義了一個自訂查詢,以根據 lastName 檢索 Person 物件的清單。您將在本指南的後續章節中看到如何調用它。

使應用程式可執行

雖然可以將此服務封裝為傳統的 WAR 檔案以部署到外部應用程式伺服器,但下面示範的更簡單方法是建立獨立應用程式。您將所有內容封裝在單個可執行的 JAR 檔案中,由一個傳統的 Java main() 方法驅動。在此過程中,您使用 Spring 對嵌入 Tomcat Servlet 容器作為 HTTP 執行階段的支援,而不是部署到外部 Servlet 容器。

src/main/java/hello/Application.java

package hello;

import org.apache.geode.cache.client.ClientRegionShortcut;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;

@SpringBootApplication
@ClientCacheApplication(name = "AccessingGemFireDataRestApplication")
@EnableEntityDefinedRegions(
  basePackageClasses = Person.class,
  clientRegionShortcut = ClientRegionShortcut.LOCAL
)
@EnableGemfireRepositories
@SuppressWarnings("unused")
public class Application {

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

@SpringBootApplication 是一個方便的註解,它添加了以下所有內容

  • @Configuration:將類別標記為應用程式內容的 Bean 定義來源。

  • @EnableAutoConfiguration:告知 Spring Boot 開始根據類別路徑設定、其他 Bean 和各種屬性設定添加 Bean。例如,如果 spring-webmvc 在類別路徑上,則此註解將應用程式標記為 Web 應用程式,並啟用關鍵行為,例如設定 DispatcherServlet

  • @ComponentScan:告知 Spring 在 hello 套件中尋找其他組件、組態和服務,使其找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法來啟動應用程式。您是否注意到沒有單行 XML?也沒有 web.xml 檔案。此 Web 應用程式是 100% 純 Java,您不必處理任何管線或基礎結構的設定。

@EnableGemfireRepositories 註解啟用了 Spring Data for Apache Geode RepositoriesSpring Data for Apache Geode 將建立 PersonRepository 介面的具體實作,並將其配置為與 Apache Geode 的嵌入式實例進行通訊。

建置可執行的 JAR

您可以使用 Gradle 或 Maven 從命令列執行應用程式。您也可以建置一個單個可執行的 JAR 檔案,其中包含所有必要的依賴項、類別和資源並執行該檔案。建置可執行 JAR 使您可以輕鬆地在整個開發生命週期、跨不同環境等情況下,發布、版本控制和部署服務作為應用程式。

如果您使用 Gradle,您可以使用 ./gradlew bootRun 執行應用程式。或者,您可以使用 ./gradlew build 建置 JAR 檔案,然後執行 JAR 檔案,如下所示

java -jar build/libs/gs-accessing-gemfire-data-rest-0.1.0.jar

如果您使用 Maven,您可以使用 ./mvnw spring-boot:run 執行應用程式。或者,您可以使用 ./mvnw clean package 建置 JAR 檔案,然後執行 JAR 檔案,如下所示

java -jar target/gs-accessing-gemfire-data-rest-0.1.0.jar
此處描述的步驟建立可執行的 JAR。您也可以建置傳統的 WAR 檔案

將顯示記錄輸出。該服務應在幾秒鐘內啟動並執行。

測試應用程式

現在應用程式正在執行,您可以對其進行測試。您可以使用任何您想要的 REST 客户端。以下範例使用 *nix 工具 curl

首先,您要查看頂層服務。

$ curl https://127.0.0.1:8080
{
  "_links" : {
    "people" : {
      "href" : "https://127.0.0.1:8080/people"
    }
  }
}

在這裡,您可以初步了解此伺服器提供的內容。有一個 people 連結位於 https://127.0.0.1:8080/peopleSpring Data for Apache Geode 不支援像其他 Spring Data REST 指南那樣的分頁,因此沒有額外的導航連結。

Spring Data REST 使用 HAL 格式進行 JSON 輸出。它很靈活,並提供了一種方便的方法來提供與所服務資料相鄰的連結。
$ curl https://127.0.0.1:8080/people
{
  "_links" : {
    "search" : {
      "href" : "https://127.0.0.1:8080/people/search"
    }
  }
}

是時候建立新的 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/1
Content-Length: 0
Date: Wed, 05 Mar 2014 20:16:11 GMT
  • -i 確保您可以查看包含標頭的回應訊息。新建立的 Person 的 URI 顯示出來

  • -X POST 發出 POST HTTP 請求以建立新條目

  • -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" : {
    "search" : {
      "href" : "https://127.0.0.1:8080/people/search"
    }
  },
  "_embedded" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "https://127.0.0.1:8080/people/1"
        }
      }
    } ]
  }
}

people 集合資源包含 Frodo 的清單。請注意它如何包含 self 連結。Spring Data REST 也使用 Evo Inflector 來複數化實體的名稱以進行分組。

您可以直接查詢單個記錄

$ curl https://127.0.0.1:8080/people/1
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/1"
    }
  }
}
這可能看起來純粹是基於 Web 的,但在幕後,它正在與嵌入式 Apache Geode 資料庫進行通訊。

在本指南中,只有一個網域物件。對於更複雜的系統,其中網域物件彼此相關,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" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "https://127.0.0.1:8080/people/1"
        }
      }
    } ]
  }
}

由於您將其定義為在程式碼中傳回 List<Person>,因此它將傳回所有結果。如果您將其定義為僅傳回 Person,它將選擇一個 Person 物件傳回。由於這可能是不可預測的,因此對於可能傳回多個條目的查詢,您可能不想這樣做。

您也可以發出 PUTPATCHDELETE REST 呼叫,以替換、更新或刪除現有記錄。

$ curl -X PUT -H "Content-Type:application/json" -d '{ "firstName": "Bilbo", "lastName": "Baggins" }' https://127.0.0.1:8080/people/1
$ curl https://127.0.0.1:8080/people/1
{
  "firstName" : "Bilbo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/1"
    }
  }
}
$ curl -X PATCH -H "Content-Type:application/json" -d '{ "firstName": "Bilbo Jr." }' https://127.0.0.1:8080/people/1
$ curl https://127.0.0.1:8080/people/1
{
  "firstName" : "Bilbo Jr.",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/1"
    }
  }
}
PUT 替換整個記錄。未提供的欄位將替換為 nullPATCH 可用於更新項目子集。

您可以刪除記錄

$ curl -X DELETE https://127.0.0.1:8080/people/1
$ curl https://127.0.0.1:8080/people
{
  "_links" : {
    "search" : {
      "href" : "https://127.0.0.1:8080/people/search"
    }
  }
}

這種超媒體驅動介面的一個非常方便的方面是,您可以使用 curl(或您正在使用的任何 REST 客户端)發現所有 RESTful 端點。無需與您的客戶交換正式合約或介面文件。

摘要

恭喜!您剛剛開發了一個具有基於超媒體的 RESTful 前端和基於 Apache Geode 的後端的應用程式。

取得程式碼