概述
我们的数据加载器已准备好批量处理我们的便利设施 ID,并处理对 REST 端点的请求。它已正式注册到我们的应用程序,但尚未在任何地方使用。
本课程将
- 更新我们的数据提取器,以将责任委托给数据加载器
使用我们的数据加载器
我们已完成将数据加载器引入应用程序计划的前两个步骤。
- 我们创建了一个数据加载器类。
- 我们为我们的类提供了一个
load
方法。
现在,我们要更新Listing.amenities
field的数据提取器方法。它不会直接调用我们的ListingService
方法,而是将责任委托给数据加载器,以便收集我们的查询中所有房源的便利设施数据并提供这些数据。
步骤 3 - 更新我们的数据提取器
回到datafetchers/ListingDataFetcher
,我们要进行一些更改。
让我们从我们将需要的一些额外导入开始。
import org.dataloader.DataLoader;import java.util.concurrent.CompletableFuture;
在我们的方法中,我们删除了调用 ListingService
的 amenitiesRequest
这一行。
@DgsData(parentType = "Listing")public List<Amenity> amenities(DgsDataFetchingEnvironment dfe) throws IOException {ListingModel listing = dfe.getSource();String id = listing.getId();Map<String, Boolean> localContext = dfe.getLocalContext();if (localContext.get("hasAmenityData")) {return listing.getAmenities();}- return listingService.amenitiesRequest(id);}
现在,我们将更新 our datafetcher 以改为调用 data loader。我们可以在我们的应用程序中访问任何已注册的数据加载器(任何应用了 @DgsDataLoader
注解的类)通过调用 dfe.getDataLoader()
方法,并提供一个名称。
if (localContext.get("hasAmenityData")) {return listing.getAmenities();}dfe.getDataLoader("amenities");
这样会返回 DataLoader
类型有两个类型 变量:第一个描述传递到 data loader 中的标识符的数据类型。第二个描述为每个标识符返回的数据类型。我们正在传入 String
类型的我们的 listing ID,对于每个标识符,我们希望获得 List
的 Amenity
类型。
DataLoader<String, List<Amenity>> amenityDataLoader = dfe.getDataLoader("amenities");
我们现在我们可以调用我们 amenityDataLoader
的 load
方法,传入我们要解析便利设施数据的 listing 的 id
。
DataLoader<String, List<Amenity>> amenityDataLoader = dfe.getDataLoader("amenities");return amenityDataLoader.load(id);
我们很快就会在已添加的新行上看到一条红色的波浪线。我们的 IDE 抱怨,因为我们方法的返回类型是 List<Amenity>
,但我们的 data loader 的 load
方法返回的类型是 CompletableFuture<List<Amenity>>
。
我们不能简单地将该方法的返回类型更新为 CompletableFuture<List<Amenity>>
,因为如果该数据已可用,我们的方法有可能返回 List<Amenity>
类型。为了缓解这种不匹配,我们改为将我们的方法返回值类型更新为 Object
。
@DgsData(parentType = "Listing") {1}public Object amenities(DgsDataFetchingEnvironment dfe) throws IOException {ListingModel listing = dfe.getSource();String id = listing.getId();Map<String, Boolean> localContext = dfe.getLocalContext();if (localContext.get("hasAmenityData")) {return listing.getAmenities();}DataLoader<String, List<Amenity>> amenityDataLoader = dfe.getDataLoader("amenities");return amenityDataLoader.load(id);}
退一步,我们可以看到这个 Listing.amenities
datafetcher 的新流程。我们仍然有两条路径:
- 如果我们已经可以访问设施数据 (
localContext.get("hasAmenityData")
),我们可以简单地返回我们的Listing
实例中的amenities
属性。 - 否则,我们需要提出后续请求获取设施数据,我们的 datafetcher 将信息列表
id
传递给数据加载器,以将其批量分组在一个大型请求中,用于查询中的 所有 信息列表的设施。
以前,我们的 datafetcher 在收到每个 listingId
后,在新的请求中调用我们的 数据源。现在,它将每个信息的 id
传递到我们的数据加载器类,该类负责将收到的 所有 id 分组在一个请求中。
这可以让数据取用器继续实现其常规责任,即被调用来处理 Listing.amenities
的每个实例,它能够帮助解决此问题,而无需用多项网络调用来损害性能。当需要在网络上发起调用时,它会将每个 ID 传递到数据加载器,而 DGS 会负责处理剩余的事务!
运行查询
我们已经做了很多修改,因此让我们停止运行的服务器并重新启动它。
按下播放按钮,或运行以下命令。
./gradlew bootRun
让我们跳回 沙盒并确保我们的运行服务器已连接。
我们将运行与之前查询相同的查询,将目光放在应用程序终端上,以便能够通过已记录的消息来监视已发出的请求数量。
query GetFeaturedListingsAmenities {featuredListings {idtitleamenities {idnamecategory}}}
当我们运行 查询时,应当会看到返回的房源及其设施列表。
并且我们仅在终端中看到了输出的一行内容:
Calling the /amenities/listings endpoint with listings [listing-1, listing-2, listing-3]
我们的房源 ID 已成功在单次请求中得到批量处理!👏👏👏
练习
要点
- 实际上,要使用数据加载器,我们需要在数据取用器方法中调用其
load
方法。 - 每一个数据取用器方法均可利用
DgsDataFetchingEnvironment
参数,该参数包括getDataLoader
方法。 - DGS 内置数据加载器具有缓存优势,并会对它们请求的键进行重复数据删除。
恭喜!
完成这些之后,你就大功告成了!你已解决 n+1 问题,拥有工具集中的数据加载器,你就有资源可以提高数据获取器的效率。我们在应用程序中采用了基本的数据获取策略,并将其变为可以随着我们的查询和功能扩展的内容。通过使用数据加载器,我们了解到如何通过网络进行更少、更高效的请求,从而以前所未有的速度交付数据。
接下来:学习如何在 基于 Java & DGS 的 Federation 中使用全新的域来扩展 GraphQL API。我们将介绍构建联邦 图的最佳实践,以及 GraphOS工具,你将使用这些工具让通往稳健企业 API 的道路变得平稳、可观察和高性能!
感谢你参加本课程,我们迫不及待地在下一堂课中见到你。
分享你对这一课的疑问和评论
本课程当前为
你需要一个 GitHub 帐户才能进行以下操作。没有该帐户? 改为在我们的 Odyssey 论坛中发帖。