如何避免使用Spring Data Rest进行n + 1查询?

Wil*_*ler 21 rest spring spring-data spring-data-rest spring-hateoas

题.如何使用Spring Data REST避免n + 1查询?

背景.在查询Spring Data REST以获取资源列表时,每个生成的顶级资源都具有指向关联资源的链接,而不是将关联资源直接嵌入顶级资源中.例如,如果我查询数据中心列表,则关联的区域显示为链接,如下所示:

{
  "links" : [ {
    "rel" : "self",
    "href" : "http://localhost:2112/api/datacenters/1"
  }, {
    "rel" : "datacenters.DataCenter.region",
    "href" : "http://localhost:2112/api/datacenters/1/region"
  } ],
  "name" : "US East 1a",
  "key" : "amazon-us-east-1a"
}
Run Code Online (Sandbox Code Playgroud)

然而,非常典型的是,想要获得相关信息而不必进行n + 1次查询.为了坚持上面的例子,我可能想要在UI中显示数据中心及其相关区域的列表.

我试过的.我在my上创建了一个自定义查询,RegionRepository以获取给定数据中心键集的所有区域:

@RestResource(path = "find-by-data-center-key-in")
Page<Region> findByDataCentersKeyIn(
    @Param("key") Collection<String> keys,
    Pageable pageable);
Run Code Online (Sandbox Code Playgroud)

遗憾的是,此查询生成的链接与上面的数据中心查询生成的链接不重叠.以下是我为自定义查询获得的链接:

http://localhost:2112/api/regions/search/find-by-data-center-key-in?key=amazon-us-east-1a&key=amazon-us-east-1b

{
  "links" : [ ],
  "content" : [ {
    "links" : [ {
      "rel" : "self",
      "href" : "http://localhost:2112/api/regions/1"
    }, {
      "rel" : "regions.Region.datacenters",
      "href" : "http://localhost:2112/api/regions/1/datacenters"
    }, {
      "rel" : "regions.Region.infrastructureprovider",
      "href" : "http://localhost:2112/api/regions/1/infrastructureprovider"
    } ],
    "name" : "US East (N. Virginia)",
    "key" : "amazon-us-east-1"
  }, {
    "links" : [ {
      "rel" : "self",
      "href" : "http://localhost:2112/api/regions/1"
    }, {
      "rel" : "regions.Region.datacenters",
      "href" : "http://localhost:2112/api/regions/1/datacenters"
    }, {
      "rel" : "regions.Region.infrastructureprovider",
      "href" : "http://localhost:2112/api/regions/1/infrastructureprovider"
    } ],
    "name" : "US East (N. Virginia)",
    "key" : "amazon-us-east-1"
  } ],
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 1
  }
}
Run Code Online (Sandbox Code Playgroud)

挑战似乎是,一旦您已经了解了数据的形状,数据中心查询就会返回不具有特别信息的链接.例如,我已经知道数据中心1的区域位于/datacenters/1/region,所以如果我想要了解涉及哪个特定区域的实际信息,我必须按照链接来获取它.特别是我必须按照链接获取在批量查询中显示的规范URI,这将允许我避免n + 1个查询.

Oli*_*ohm 20

Spring Data REST的工作原理如下:默认情况下,我们假设每个应用程序存储库都是REST服务的主要资源.因此,如果您公开实体的相关对象的存储库,您将获得呈现给它的链接,并且我们通过嵌套资源(例如foo/{id}/bar)将一个实体的分配暴露给另一个实体.

要防止这种情况,请注释相关的存储库接口,@RestResource(exported = false)以防止此存储库管理的实体成为顶级资源.

更通用的方法是从Spring Data REST开始,让您公开要管理的资源并应用默认规则.然后,您可以通过实现ResourceProcessor<T>和将实现注册为Spring bean 来自定义呈现和链接.然后,ResourceProcessor将允许您自定义呈现的数据,添加到表示的链接等.

对于其他一切,手动实现控制器(可能混合到默认控制器的URI空间)并通过ResourceProcessor实现添加链接.在Spring RESTBucks示例中可以看到一个示例.该示例项目使用Spring Data REST来管理Order实例并实现自定义控制器以实现更复杂的支付流程.除此之外,它还添加了一个指向Order资源的链接,指向手动实现的代码.

  • 另请注意,这不是一次性的。我的大多数顶级资源(有几十个——这是一个配置管理系统)都有我希望能够在列表视图中显示的关联。是否认为我会在所有这些情况下编写自定义控制器? (2认同)
  • 您可以让 `ResourceProcessor` 使用存储库查找关联区域并将它们添加到包装的数据中心实例中。另一种选择是在“RegionRepository”上公开一个查找器,以检索数据中心的所有区域并从客户端触发它。 (2认同)

Jon*_*bin 6

如果在Jackson ObjectMapper中配置的序列化程序是通过查看a触发的,则Spring Data REST将仅创建您描述的表示PersistentEntityResource形式,Resource这是Spring Data REST中使用的一种特殊类型.

如果您创建ResourceProcessor<Resource<MyPojo>>并返回a new Resource<MyPojo>(origResource.getContent(), origResource.getLinks()),则不会触发默认的Spring Data REST序列化机制,并且将应用Jackson的正常序列化规则.

但请注意,Spring Data REST之所以进行关联的原因是因为在序列化为JSON时,任意停止遍历对象图是非常困难的.通过以它的方式处理关联,它保证了序列化程序不会开始遍历N级深度的对象图,并且在性能和表现的性能上变得更慢.

确保Jackson不会尝试序列化a PersistentEntityResource,这是它在默认配置中所做的事情,将确保不会触发任何Spring Data REST关联处理.当然,这方面的缺点是Spring Data REST的助手都不会被触发.如果您仍然需要指向相关资源的链接,则必须确保自己创建这些资源并将其添加到传出平面Resource.