Spring Boot 与 Docker
观察 GraphQL 的实际运行

本指南将引导您使用 Apache Geode 数据管理系统来构建应用程序。

你要构建的内容

你将使用 Spring Data for Apache Geode 来存储和检索 POJOs。

所需内容

如何完成本指南

和大多数 Spring 入门指南一样,你可以从零开始并完成每一步,也可以跳过已熟悉的基本设置步骤。不管选择哪种方式,你最终都能获得可运行的代码。

要从头开始,请参阅使用 Spring Initializr 开始

若要跳过基础知识,请执行以下操作:

完成之后,你可以将你的结果与 gs-accessing-data-gemfire/complete 中的代码进行对比。

从 Spring Initializr 开始

对于所有的 Spring 应用程序,你应该从 Spring Initializr 开始。Spring Initializr 可以快速引入你所需的所有依赖,并为你完成许多设置工作。这个示例需要 Spring for Apache Geode 依赖。

你可以使用这个预初始化的项目,并点击 Generate 下载 ZIP 文件。该项目已配置为适用于本教程中的示例。

手动初始化项目:

  1. 在网页浏览器中访问 https://start.spring.io。该服务会为你拉取所有必要的依赖项,并自动完成大部分配置工作。

  2. 请选择所需的构建工具(Gradle 或 Maven)和编程语言。本指南假设您选择了 Java。

  3. 点击依赖项,然后选择Spring for Apache Geode

  4. 点击生成

  5. 下载生成的ZIP文件,这是根据您的选择配置的一个Web应用程序的归档文件。

如果你的IDE集成了Spring Initializr,你可以在IDE中直接完成这个过程。
你也可以从 Github 获取该项目,并在你的 IDE 或其他编辑器中打开它。

定义简单实体

Apache Geode 是一个内存数据网格(IMDG),它将数据映射到区域。你可以配置分布式区域,以便在集群中的多个节点之间分区和复制数据。然而,在本指南中,我们使用 LOCAL 区域,这样你无需设置额外的服务器集群等。

Apache Geode 是一个键值存储,而区域实现了 java.util.concurrent.ConcurrentMap 接口。虽然你可以将区域视为 java.util.Map,但由于数据在区域内是分布式的、复制的,并且通常由区域进行管理,因此它比简单的 Java Map 要复杂得多。

在这个例子中,你将使用几个注解,把 Person 对象存储到 Apache Geode 的一个区域中。

src/main/java/hello/Person.java

package hello;

import java.io.Serializable;

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

import lombok.Getter;

@Region(value = "People")
public class Person implements Serializable {

  @Id
  @Getter
  private final String name;

  @Getter
  private final int age;

  @PersistenceConstructor
  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

  @Override
  public String toString() {
    return String.format("%s is %d years old", getName(), getAge());
  }
}

这里有一个包含两个字段(nameage)的 Person 类。你还有一个用于在创建新实例时填充实体的单个持久化构造函数。该类使用Project Lombok 来简化实现。

请注意,此类使用了@Region("People")注解。当Apache Geode存储此类的一个实例时,会在People区域中创建一个新的条目。该类还用@Id标记了name字段,这表示用于在Apache Geode内部标识和跟踪Person数据的唯一标识符。本质上,带有@Id注解的字段(如name)是键,而Person实例则是该键/值条目的值。在Apache Geode中没有自动的键生成功能,因此必须在将实体持久化到Apache Geode之前设置ID(即name字段)。

下一个重要的是人的年龄。在本指南的后续部分,我们将使用它来构建一些查询。

重写后的 toString() 方法会输出该人的姓名和年龄。

创建简单查询

Spring Data for Apache Geode 专注于使用 Spring 在 Apache Geode 中存储和访问数据,并继承了 Spring Data Commons 项目的一些强大功能,例如推导查询的能力。因此,你无需学习 Apache Geode 的查询语言(OQL)。只需编写几个方法,框架就会自动生成相应的查询。

为了了解其工作原理,创建一个用于查询存储在 Apache Geode 中的 Person 对象的接口。

src/main/java/hello/PersonRepository.java

package hello;

import org.springframework.data.gemfire.repository.query.annotation.Trace;
import org.springframework.data.repository.CrudRepository;

public interface PersonRepository extends CrudRepository<Person, String> {

  @Trace
  Person findByName(String name);

  @Trace
  Iterable<Person> findByAgeGreaterThan(int age);

  @Trace
  Iterable<Person> findByAgeLessThan(int age);

  @Trace
  Iterable<Person> findByAgeGreaterThanAndAgeLessThan(int greaterThanAge, int lessThanAge);

}

PersonRepository 继承了 Spring Data Commons 中的 CrudRepository 接口,并为泛型类型参数指定了值和 ID(键)的具体类型,分别为 PersonString。此接口提供了许多操作,包括基本的 CRUD 操作(创建、读取、更新、删除),以及简单的查询数据访问操作,例如 findById(..)

你可以通过声明方法签名来定义其他查询。在这种情况下,我们添加了findByName,它会搜索类型为Person的对象,并找到一个在name属性上匹配的对象。

你还有:

  • findByAgeGreaterThan: 查找某一年龄以上的人

  • findByAgeLessThan: 查找低于特定年龄的人

  • findByAgeGreaterThanAndAgeLessThan: 用于查找特定年龄范围内的人

让我们来配置一下,看看效果如何!

创建应用程序类

以下示例创建了一个包含所有组件的应用程序类:

src/main/java/hello/Application.java

package hello;

import static java.util.Arrays.asList;
import static java.util.stream.StreamSupport.stream;

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

import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
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 = "AccessingDataGemFireApplication")
@EnableEntityDefinedRegions(
  basePackageClasses = Person.class,
  clientRegionShortcut = ClientRegionShortcut.LOCAL
)
@EnableGemfireRepositories
public class Application {

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

  @Bean
  ApplicationRunner run(PersonRepository personRepository) {

    return args -> {

      Person alice = new Person("Adult Alice", 40);
      Person bob = new Person("Baby Bob", 1);
      Person carol = new Person("Teen Carol", 13);

      System.out.println("Before accessing data in Apache Geode...");

      asList(alice, bob, carol).forEach(person -> System.out.println("\t" + person));

      System.out.println("Saving Alice, Bob and Carol to Pivotal GemFire...");

      personRepository.save(alice);
      personRepository.save(bob);
      personRepository.save(carol);

      System.out.println("Lookup each person by name...");

      asList(alice.getName(), bob.getName(), carol.getName())
        .forEach(name -> System.out.println("\t" + personRepository.findByName(name)));

      System.out.println("Query adults (over 18):");

      stream(personRepository.findByAgeGreaterThan(18).spliterator(), false)
        .forEach(person -> System.out.println("\t" + person));

      System.out.println("Query babies (less than 5):");

      stream(personRepository.findByAgeLessThan(5).spliterator(), false)
        .forEach(person -> System.out.println("\t" + person));

      System.out.println("Query teens (between 12 and 20):");

      stream(personRepository.findByAgeGreaterThanAndAgeLessThan(12, 20).spliterator(), false)
        .forEach(person -> System.out.println("\t" + person));
    };
  }
}

在配置中,需要添加@EnableGemfireRepositories注解。

  • 默认情况下,@EnableGemfireRepositories 会扫描当前包中继承了 Spring Data 存储库接口的所有接口。你可以使用其 basePackageClasses = MyRepository.class 参数来安全地指定 Spring Data for Apache Geode 按类型扫描不同的根包,以查找特定于应用程序的 Repository 扩展。

需要一个包含一个或多个区域的 Apache Geode 缓存来存储所有数据。为此,你可以使用 Spring Data for Apache Geode 提供的配置注解之一:@ClientCacheApplication@PeerCacheApplication@CacheServerApplication

Apache Geode 支持不同的缓存拓扑结构,例如客户端/服务器、对等(P2P)以及广域网(WAN)配置。在 P2P 模式下,对等缓存实例嵌入到应用程序中,并且您的应用程序可以作为集群中的对等缓存成员参与其中。然而,由于您的应用程序受制于集群中对等成员的所有约束条件,因此这种模式不像客户端/服务器拓扑那样常用。

在这种情况下,我们使用 @ClientCacheApplication 来创建一个“客户端”缓存实例,该实例能够连接并与其他服务器集群进行通信。然而,为了简化起见,客户端通过使用 LOCAL 客户端区域在本地存储数据,而无需设置或运行任何服务器。

现在回想一下,你是如何使用 SDG 映射注解 @Region("People")Person 标记为存储在名为 People 的区域中的?在这里,你通过定义 ClientRegionFactoryBean<String, Person> 的 bean 来创建该区域。你需要注入刚刚定义的缓存实例,并将其命名为 People

A Apache Geode 缓存实例(无论是对等实例还是客户端)只是一个存储区域的容器,这些区域保存你的数据。你可以将缓存视为关系型数据库管理系统中的模式,将区域视为表。然而,缓存还执行其他管理功能来控制和管理所有区域。
类型为<String, Person>,其中键的类型是String,值的类型是Person

public static void main 方法使用 Spring Boot 的 SpringApplication.run() 来启动应用程序,并通过应用的 Spring Data 仓库调用 ApplicationRunner(另一个 bean 定义),在 Apache Geode 上执行数据访问操作。

应用程序自动注入了你刚刚定义的 PersonRepository 实例。Spring Data for Apache Geode 动态创建了一个实现此接口的具体类,并插入所需的查询代码以满足接口的要求。在 run() 方法中使用这个 repository 实例来演示其功能。

存储和检索数据

在本指南中,你将创建三个本地的 Person 对象: AliceBaby BobTeen Carol。最初,这些对象只存在于内存中。创建之后,你需要将它们保存到 Apache Geode 中。

现在你可以运行多个查询。第一个查询是根据姓名查找所有人。接着,你可以通过年龄属性来运行几个查询,分别找出成年人、婴儿和青少年。如果开启了日志记录功能,你还可以查看 Spring Data for Apache Geode 自动为你生成的查询。

要查看 SDG 生成的 Apache Geode OQL 查询,可以将 @ClientCacheApplication 注解中的 logLevel 属性设置为 config。由于查询方法(如 findByName)使用了 SDG 的 @Trace 注解,这会开启 Apache Geode 的 OQL 查询跟踪(即查询级别的日志记录),从而显示生成的 OQL、执行时间、是否使用了任何 Apache Geode 索引来收集结果以及返回的行数。

构建可执行的 JAR 包

你可以通过 Gradle 或 Maven 从命令行运行应用程序。你也可以构建一个包含所有必需依赖项、类和资源的单个可执行 JAR 文件并运行它。这样可以轻松地在整个开发生命周期中,在不同的环境中打包、版本控制和部署服务。

如果你使用 Gradle,可以通过运行 ./gradlew bootRun 来启动应用程序。或者,你也可以通过运行 ./gradlew build 构建 JAR 文件,并按如下方式运行该 JAR 文件:

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

如果你使用 Maven,可以通过运行 ./mvnw spring-boot:run 来启动应用。或者,你可以通过执行 ./mvnw clean package 构建 JAR 文件,然后按照以下方式运行该 JAR 文件:

java -jar target/gs-accessing-data-gemfire-0.1.0.jar
这里描述的步骤创建了一个可执行的 JAR 文件。你也可以构建一个经典的 WAR 文件

你应该看到类似的内容(可能包括其他项,例如查询):

Before linking up with {apache-geode-name}...
	Alice is 40 years old.
	Baby Bob is 1 years old.
	Teen Carol is 13 years old.
Lookup each person by name...
	Alice is 40 years old.
	Baby Bob is 1 years old.
	Teen Carol is 13 years old.
Adults (over 18):
	Alice is 40 years old.
Babies (less than 5):
	Baby Bob is 1 years old.
Teens (between 12 and 20):
	Teen Carol is 13 years old.

摘要

恭喜您!您已成功设置了 Apache Geode 缓存客户端,存储了简单实体,并开发了快速查询。

本页目录