使用 Cloud Datastore 的 Spring Boot 应用

Google Cloud Datastore 是一个 NoSQL 文档数据库,以可自动扩展、高性能和易于进行应用开发为设计宗旨。

学习内容

  • 如何在 Spring Boot 中使用 Cloud Datastore 保存和检索 Java 对象

所需条件

  • 一个 Google Cloud Platform 项目
  • 一个浏览器,例如 ChromeFirefox

您将如何使用本教程?

仅阅读教程内容 阅读并完成练习

您如何评价自己在使用 Google Cloud Platform 服务方面的经验水平?

新手水平 中等水平 熟练水平

自定进度的环境设置

如果您还没有 Google 帐号(Gmail 或 Google Apps),则必须创建一个。登录 Google Cloud Platform Console (console.cloud.google.com) 并创建一个新项目:

2016-02-10 12:45:26 的屏幕截图.png

请记住项目 ID,它在所有 Google Cloud 项目中都是唯一名称(很抱歉,上述名称已被占用,您无法使用!)。它稍后将在此 Codelab 中被称为 PROJECT_ID

接下来,您需要在 Cloud Console 中启用结算功能,才能使用 Google Cloud 资源。

在此 Codelab 中运行仅花费几美元,但是如果您决定使用更多资源或继续让它们运行,费用可能更高(请参阅本文档末尾的“清理”部分)。

Google Cloud Platform 的新用户有资格获享 $300 免费试用

激活 Google Cloud Shell

在 GCP 控制台中,点击右上角工具栏上的 Cloud Shell 图标:

然后点击“启动 Cloud Shell”:

配置和连接到环境应该只需要片刻时间:

这个虚拟机已加载了您需要的所有开发工具。它提供了一个永久性的 5GB 主目录,并且在 Google Cloud 上运行,从而大大增强了网络性能和身份验证功能。只需使用一个浏览器或 Google Chromebook 即可完成本实验中的大部分(甚至全部)工作。

在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设置为您的 PROJECT_ID。

在 Cloud Shell 中运行以下命令以确认您已通过身份验证:

gcloud auth list

命令输出

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

命令输出

[core]
project = <PROJECT_ID>

如果不是上述结果,您可以使用以下命令进行设置:

gcloud config set project <PROJECT_ID>

命令输出

Updated property [core/project].

在 GCP Console 中,导航到菜单 -> Datastore(位于“存储”部分)

如果您从未在当前项目中使用过 Datastore,则会看到“选择 Cloud Firestore 模式”屏幕。选择“Datastore 模式”选项。

然后,您会看到“选择数据的存储位置”屏幕。选择 us-east1 或任何其他地区位置,然后点击“创建数据库”:

在 CloudShell 环境中,使用以下命令初始化和启动新的 Spring Boot 应用:

$ curl https://start.spring.io/starter.tgz \
  -d packaging=war \
  -d dependencies=cloud-gcp \
  -d baseDir=datastore-example \
  -d bootVersion=2.1.1.RELEASE | tar -xzvf -

这将使用一个新的 Maven 项目、Maven 的 pom.xml(一个 Maven 封装容器)和一个应用入口点来创建一个新的 datastore-example/ 目录。

我们的应用将提供一个 CLI 供用户输入命令和查看结果。我们将创建一个类来表示图书,然后使用 Datastore Repository 将其保存到 Cloud Datastore。

我们还需要向 pom.xml 再添加一个必要的依赖项。

点击 Cloud Shell 菜单中的启动代码编辑器,打开网页代码编辑器。

编辑器加载后,修改 pom.xml 文件以添加 Spring Data Cloud Datastore Spring Boot 入门依赖项:

pom.xml

<project>
  ...
  <dependencies>
        ...
        <!-- Add GCP Datastore Starter -->
        <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-gcp-starter-data-datastore</artifactId>
        </dependency>

        <!-- Add Spring Shell Starter -->
        <dependency>
                <groupId>org.springframework.shell</groupId>
                <artifactId>spring-shell-starter</artifactId>
                <version>2.0.0.RELEASE</version>
        </dependency>

  </dependencies>
</project>

使用编辑器创建包含以下内容的 Book 类:

datastore-example/src/main/java/com/example/demo/Book.java

package com.example.demo;

import org.springframework.cloud.gcp.data.datastore.core.mapping.Entity;
import org.springframework.data.annotation.Id;

@Entity(name = "books")
public class Book {
        @Id
        Long id;

        String title;

        String author;

        int year;

        public Book(String title, String author, int year) {
                this.title = title;
                this.author = author;
                this.year = year;
        }

        public long getId() {
                return this.id;
        }

        @Override
        public String toString() {
                return "Book{" +
                                "id=" + this.id +
                                ", title='" + this.title + '\'' +
                                ", author='" + this.author + '\'' +
                                ", year=" + this.year +
                                '}';
        }
}

如您所见,这是一个简单的 POJO。该类带有 @Entity 注释,表示它可以存储在 Datastore 中并提供种类名称(将种类视为 SQL 数据库中的一个表,请参阅文档了解更多详情)。种类名称是可选的 - 如果省略它,系统将根据类名称生成种类名称。

请注意,我们已为 id 属性添加了 @Id 注释。这表示我们希望将此字段用作 Datastore 键的标识符部分。每个 Datastore 实体都需要一个标识符。支持的类型为 StringLong

我们会重写 toString 方法,使对象的字符串表示形式更具可读性;在我们将其打印出时,这非常有用。

别忘了保存文件!

使用以下内容创建 BookRepository 类:

datastore-example/src/main/java/com/example/demo/BookRepository.java

package com.example.demo;

import java.util.List;

import org.springframework.cloud.gcp.data.datastore.repository.DatastoreRepository;

public interface BookRepository extends DatastoreRepository<Book, Long> {

  List<Book> findByAuthor(String author);

  List<Book> findByYearGreaterThan(int year);

  List<Book> findByAuthorAndYear(String author, int year);

}

该接口扩展了 DatastoreRepository<Book, Long>,其中 Book 是网域类,LongId 类型。我们在代码库中声明了三种查询方法,系统将在后台为其自动生成实现。

第一种是 findByAuthor。就像您猜到的,此方法的实现会执行一个查询,该查询将在条件过滤器中使用用户提供的值来等效于作者字段。

findByYearGreaterThan 方法执行的查询会过滤大于用户提供的值的年份字段。

findByAuthorAndYear 执行的查询会查找作者字段和年份字段与用户提供的值匹配的实体。

打开主应用 DemoApplication 类,并按如下所示对其进行修改:

datastore-example/src/main/java/com/example/demo/DemoApplication.java

package com.example.demo;

import java.util.List;

import com.google.common.collect.Lists;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
@SpringBootApplication
public class DemoApplication {
  @Autowired
  BookRepository bookRepository;

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

  @ShellMethod("Saves a book to Cloud Datastore: save-book <title> <author> <year>")
  public String saveBook(String title, String author, int year) {
     Book savedBook = this.bookRepository.save(new Book(title, author, year));
     return savedBook.toString();
  }

  @ShellMethod("Loads all books")
  public String findAllBooks() {
     Iterable<Book> books = this.bookRepository.findAll();
     return Lists.newArrayList(books).toString();
  }

  @ShellMethod("Loads books by author: find-by-author <author>")
  public String findByAuthor(String author) {
     List<Book> books = this.bookRepository.findByAuthor(author);
     return books.toString();
  }

  @ShellMethod("Loads books published after a given year: find-by-year-after <year>")
  public String findByYearAfter(int year) {
     List<Book> books = this.bookRepository.findByYearGreaterThan(year);
     return books.toString();
  }

  @ShellMethod("Loads books by author and year: find-by-author-year <author> <year>")
  public String findByAuthorYear(String author, int year) {
     List<Book> books = this.bookRepository.findByAuthorAndYear(author, year);
     return books.toString();
  }

  @ShellMethod("Removes all books")
  public void removeAllBooks() {
     this.bookRepository.deleteAll();
  }
}

请注意我们是如何为类添加 @ShellComponent 注释的。这会告知 Spring 我们希望将此类用作 CLI 命令的来源。带 @ShellMethod 注释的方法将在应用中以 CLI 命令的形式公开。

在这里,我们使用在 BookRepository 接口中声明的方法:findByAuthorfindByYearGreaterThanfindByAuthorAndYear。此外,我们还使用三种内置方法:savefindAlldeleteAll

我们来看一下 saveBook 方法。我们使用用户提供的标题、作者和年份值创建一个 Book 对象。如您所见,我们未提供 id 值,因此系统会在保存时自动为其分配 ID 字段。save 方法接受类型为 Book 的对象,并将其保存到 Cloud Datastore。它会返回一个 Book 对象,其中填充了所有字段,包括 id 字段。最后,我们返回此对象的字符串表示形式。

其余方法的工作方式类似:它们接受将参数传递给相应的代码库方法,并返回字符串化结果。

如需构建并启动应用,请在 Cloud Shell 中(通过 pom.xml 所在项目的 datastore-example/ 根目录)执行此命令:

$ mvn spring-boot:run

构建阶段成功后,系统将显示 Spring 徽标并显示 Shell 提示:

 .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.1.RELEASE)

shell:> 

现在,您可以对我们之前定义的命令进行实验。要查看命令列表,请使用 help 命令:

shell:> help
...
find-all-books: Loads all books
find-by-author: Loads books by author: find-by-author <author>
find-by-author-year: Loads books by author and year: find-by-author-year <author> <year>
find-by-year-after: Loads books published after a given year: find-by-year-after <year>
remove-all-books: Removes all books
save-book: Saves a book to Cloud Datastore: save-book <title> <author> <year>

请尝试以下操作:

  1. 使用 save-book 命令创建几本图书
  2. 使用 find-all-books 命令运行搜索
  3. 查找特定作者的图书 (find-by-author <author>)
  4. 查找在特定年份之后出版的图书 (find-by-year-after <year>)
  5. 按特定作者和年份查找图书 (find-by-author-year <author> <year>)

要查看实体在 Cloud Datastore 中的存储方式,请转到 GCP Console,然后导航到菜单 -> Datastore(位于“存储”部分)-> 实体(如有必要,请选择“[默认]”命名空间和“图书”种类)。

如需清理,请从应用 Shell 中使用适当命名的 remove-all-books 命令移除所有图书。

shell:> remove-all-books

要退出应用,请使用退出命令,然后按 Ctrl+C。

在此 Codelab 中,您创建了一个交互式 CLI 应用,该应用可以在 Cloud Datastore 中存储和检索对象!

了解详情

许可

此作品已获得 Creative Commons Attribution 2.0 通用许可授权。