5. 数据获取器
10m

概述

但在我们可以我们的服务器以获取一些房源数据之前,我们需要定义如何准确地检索这些数据。

在本课中,我们将

  • 了解 数据获取器 以及它们在 DGS 中的工作原理
  • 探索 DGS 代码生成
  • 返回一些硬编码的模拟数据

🛠 数据获取器第一步

我们正在构建一个 ,它可以:

  1. 接收来自客户端的传入
  2. 根据我们的模式验证
  3. 检索查询模式 的数据
  4. 并以响应形式返回数据

我们已经定义了我们的模式,因此 DGS 可以开箱即用地处理步骤 1 和步骤 2。

我们现在需要定义如何实际 检索返回 被查询时的数据。我们将把所有这些指令封装在一个名为 数据获取器 的方法中。(在其他框架中,您可能会看到数据获取器被描述为 解析器函数。)

我们使用数据获取器将 映射到可以完成它们的逻辑。换句话说,我们可以编写单独的方法,这些方法在查询某些模式字段(例如我们的 Query 类型的 featuredListings )时调用。

type Query {
featuredListings: [Listing!]!
}

这些方法有责任以我们的模式所期望的形状返回数据——例如,我们为 featuredListings 编写的 数据获取器应该返回一个与 Listing 类型匹配的对象列表。

让我们定义一个类来包含我们的数据获取器方法。

✏️ 编写数据获取器

为了使我们的代码井井有条,让我们在我们的 java/com.example.listings 包中创建一个新的 datafetchers 目录。

📂 main
┣ 📂 java
┃ ┣ 📂 com.example.listings
┃ ┃ ┃ ┣ 📂 datafetchers
┃ ┃ ┃ ┣ 📄 ListingsApplication
┃ ┃ ┃ ┣ 📄 WebConfiguration

在里面,我们可以创建一个名为 ListingDataFetcher 的新类文件。

datafetchers/ListingDataFetcher
package com.example.listings.datafetchers;
public class ListingDataFetcher {
}

我们的空类已准备好进行一些方法来执行实际的数据获取,但我们首先需要使用一个特殊的注释来标记它,该注释告诉 DGS 在组装我们的 API 的部分时将其包含在内。

此注释称为 @DgsComponent,我们所需要做的就是从 DGS 包中导入它,并将其放在类定义的顶部。现在,它正式成为 DGS 团队的一员了!

package com.example.listings.datafetchers;
import com.netflix.graphql.dgs.DgsComponent;
@DgsComponent
public class ListingDataFetcher {
}

接下来,让我们给这个类一个名为 featuredListings 的基本方法,以匹配我们模式中的 Query

public void featuredListings() {
// specific featuredListings-fetching logic goes here
}

为了作为一个数据获取器,一个方法需要指定 哪个 它负责。为了澄清这一点,我们有额外的 DGS 注释,可以为我们完成大部分繁重的工作。

因为 featuredListings 方法负责 Query 类型上的 featuredListings ,我们可以使用 @DgsQuery 注释。(不要忘记从我们的 DGS 包中导入它!)

package com.example.listings.datafetchers;
import com.netflix.graphql.dgs.DgsComponent;
import com.netflix.graphql.dgs.DgsQuery;
@DgsComponent
public class ListingDataFetcher {
@DgsQuery
public void featuredListings() {
// specific featuredListings-fetching logic goes here
}
}

为了使 @DgsQuery 开箱即用,我们需要确保我们的方法具有与它解析的 Query 相同的名称。这为 DGS 提供了所有必要的信息,以将 ListingDataFetcher.featuredListings 标记为 官方 Query.featuredListings 模式 的数据获取器。

还有一个大问题:我们的 featuredListings 方法实际上没有返回任何内容。但在我们的模式中,我们说对 featuredListings 的查询应该返回一个 Listing 类型列表!

type Query {
"A curated array of listings to feature on the homepage"
featuredListings: [Listing!]!
}

我们需要调整我们的方法,以便 featuredListings 实际上返回我们所说的数据类型。但在我们的 schema.graphqls 文件之外,我们实际上从哪里获取这些类型定义以供我们的数据获取器使用呢?

代码生成

我们知道我们的 Listing 类型需要哪些属性,因此我们 可以 定义一个相应的 Listing 类来表示 Java 中的每个对象。这种方法允许我们设置所有属性,包括 getter 和 setter 方法,同时保持对在数据传送到我们的客户端时可能需要执行的任何转换的精细控制。

另一种选择是使用 DGS 的代码生成插件。此工具会读取我们的模式文件,并根据我们编写的 类型生成类。使用这种方法, 在 GraphQL 类型上(如 Listingtitledescription)将成为相应类上的可获取和可设置的属性。

每种方法都有其优点,因此我们将从两者中汲取一些精华。我们将使用代码生成作为起点,并在开发过程中将自定义逻辑附加到我们的类。让我们开始吧!

生成基本类

打开项目的 build.gradle 文件并查看顶部的 plugins。启用 DGS 中代码生成的包,dgs.codegen,已在此处列出。

build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.5'
id 'io.spring.dependency-management' version '1.1.4'
id 'com.netflix.dgs.codegen' version '6.0.3'
}

插件在代码构建过程中自动运行,实际上它在我们运行代码时生效。

再次启动您的服务器,可以使用 IDE 的运行按钮,也可以在终端运行以下命令。

./gradlew bootRun
任务!

build.generated.sources 文件夹中,我们将找到一些新的包,包括 dgs-codegendgs-codegen-generated-examples

📂 build
┣ 📂 classes
┣ 📂 generated
┃ ┣ 📂 sources
┃ ┃ ┣ 📂 annotationProcessor
┃ ┃ ┣ 📂 dgs-codegen
┃ ┃ ┣ 📂 dgs-codegen-generated-examples
┃ ┃ ┣ 📂 headers
┣ 📂 resources
┗ 📂 tmp

dgs-codegen 包包含插件从读取我们的 schema.graphqls 文件生成的 Java 类型。如果我们深入到 dgs-codegen.com.example.listings.generated.types 文件中,我们将看到生成的 Listing 文件。

📂 dgs-codegen
┣ 📂 com.example.listings.generated
┃ ┣ 📂 types
┃ ┃ ┣ 📄 Listing

仔细观察生成的 Listing 类,它揭示了属性、getter 和 setter 的集合,使我们能够创建与架构中 Listing 类型规范匹配的对象。我们还将发现插件为我们添加了许多其他方法,使使用该类和构建新实例变得更加容易。

我们将基于此生成的 Listing 类进行构建,并在子类中扩展它。

让我们在代码中创建一个子类所在的位置。回到 java/com.example.listings 中,我们将定义一个名为 models 的新包,放在 datafetchers 旁边。此目录将保存我们在使用来自 的数据时使用的类。

📂 main
┣ 📂 java
┃ ┣ 📂 com.example.listings
┃ ┃ ┃ ┣ 📂 datafetchers
┃ ┃ ┃ ┣ 📂 models
┃ ┃ ┃ ┣ 📄 ListingsApplication
┃ ┃ ┃ ┣ 📄 WebConfiguration

接下来,我们将定义一个名为 ListingModel 的新类。此类将扩展生成的 Listing 类。以下是它的样子:

models/ListingModel
package com.example.listings.models;
import com.example.listings.generated.types.Listing;
public class ListingModel extends Listing {
// custom logic will live here
}

我们可以立即使用 ListingModel 类——回到我们的 featuredListings 数据提取器中。

返回数据

现在回到我们的 datafetchers/ListingDataFetcher 文件中,我们将导入 models/ListingModel 类,以及 Java List 实用程序。

datafetchers/ListingDataFetcher
import com.example.listings.models.ListingModel;
import java.util.List;

立即,我们可以将方法的返回类型更新为 ListListingModel 类型。

@DgsQuery
public List<ListingModel> featuredListings() {
}

首先,让我们从新生成的类中创建一些新的 ListingModel 实例。

@DgsQuery
public List<ListingModel> featuredListings() {
ListingModel meteorListing = new ListingModel();
}

我们需要为我们的 ListingModel 实例提供一些属性,例如 idtitlecostPerNight;我们可以通过调用属性 setter 方法将它们链接起来。(为这些 随意指定值,但请记住要与我们在架构中为它们指定的类型匹配!

ListingModel meteorListing = new ListingModel();
meteorListing.setId("1");
meteorListing.setTitle("Beach house on the edge of the Laertes meteor");
meteorListing.setCostPerNight(360.00);
meteorListing.setClosedForBookings(false);
meteorListing.setNumOfBeds(3);

太好了!这是一个新的 ListingModel 实例,所有属性都已准备就绪。

为了使我们的 List 更健壮,我们将添加至少一个 ListingModel 实例。

@DgsQuery
public List<ListingModel> featuredListings() {
ListingModel meteorListing = new ListingModel();
meteorListing.setId("1");
meteorListing.setTitle("Beach house on the edge of the Laertes meteor");
meteorListing.setCostPerNight(360.00);
meteorListing.setClosedForBookings(false);
meteorListing.setNumOfBeds(3);
ListingModel gasGiantListing = new ListingModel();
gasGiantListing.setId("2");
gasGiantListing.setTitle("Unforgettable atmosphere, unbeatable heat, tasteful furnishings");
gasGiantListing.setCostPerNight(124.00);
gasGiantListing.setClosedForBookings(true);
gasGiantListing.setNumOfBeds(4);
return List.of(meteorListing, gasGiantListing);
}

这样,我们就设置了第一个数据提取器,准备被查询。以下是完成后的整个数据提取器文件的外观。

练习

数据提取器的主要目的是什么?

主要收获

  • 我们使用数据提取器方法在查询特定架构 时返回数据。
  • DGS 代码生成插件为我们提供了有用的起点,用于映射到我们 类型的 Java 类。

下一节

哇!在下一节课中,我们将看到所有部分是如何组合在一起的——通过发送我们的第一个查询!

上一节

分享您对此课的疑问和评论

本课程目前处于

测试版
.您的反馈将帮助我们改进!如果您遇到困难或困惑,请告诉我们,我们会帮助您。所有评论都是公开的,必须遵循 Apollo 行为准则。请注意,已解决或已处理的评论可能会被删除。

您需要一个 GitHub 帐户才能在下方发帖。没有? 请在我们的 Odyssey 论坛中发帖。