使用 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 來持久化/訪問儲存在 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檔案以部署到外部應用程式伺服器,但下面演示的更簡單的方法建立了一個獨立應用程式。您將所有內容打包在一個由舊式 Java main() 方法驅動的單個可執行 JAR 檔案中。在此過程中,您使用 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://:8080
{
  "_links" : {
    "people" : {
      "href" : "https://:8080/people"
    }
  }
}

在這裡,您第一次看到了這個伺服器所能提供的功能。有一個 people 連結位於https://:8080/peopleSpring Data for Apache Geode 不支援像其他 Spring Data REST 指南那樣的分頁,因此沒有額外的導航連結。

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

是時候建立一個新的 Person 了!

$ curl -i -X POST -H "Content-Type:application/json" -d '{  "firstName" : "Frodo",  "lastName" : "Baggins" }' https://:8080/people
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Location: https://: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://:8080/people
{
  "_links" : {
    "search" : {
      "href" : "https://:8080/people/search"
    }
  },
  "_embedded" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "https://:8080/people/1"
        }
      }
    } ]
  }
}

people 集合資源包含一個 Frodo 列表。請注意它如何包含一個 self 連結。Spring Data REST 還使用Evo Inflector來將實體名稱複數化以進行分組。

您可以直接查詢單個記錄

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

在本指南中,只有一個領域物件。在一個更復雜的系統中,如果領域物件相互關聯,Spring Data REST 將呈現額外的連結以幫助導航到連線的記錄。

查詢所有自定義查詢

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

您可以看到查詢的 URL,包括 HTTP 查詢引數 name。如果您注意到,這與介面中嵌入的 @Param("name") 註解匹配。

要使用 findByLastName 查詢,請執行以下操作

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

因為您將其定義為在程式碼中返回 List<Person>,所以它將返回所有結果。如果您將其定義為僅返回 Person,它將選擇其中一個 Person 物件返回。由於這可能無法預測,因此您可能不希望對可以返回多個條目的查詢執行此操作。

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

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

您可以刪除記錄

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

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

總結

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

獲取程式碼