如何避免使用 Spring Data Rest 进行 n+1 查询? [英] How do I avoid n+1 queries with Spring Data Rest?

查看:27
本文介绍了如何避免使用 Spring Data Rest 进行 n+1 查询?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Question. How do I avoid n+1 queries with Spring Data REST?

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

Background. When querying Spring Data REST for a list of resources, each of the resulting top-level resources has links to the associated resources, as opposed to having the associated resources embedded directly in the top-level resources. For example, if I query for a list of data centers, the associated regions appear as links, like this:

{
  "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"
}

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

It is pretty typical, however, to want to get the associated information without having to do n+1 queries. To stick with the example above, I might want to display a list of data centers and their associated regions in a UI.

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

What I've tried. I created a custom query on my RegionRepository to get all the regions for a given set of data center keys:

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

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

Unfortunately the links this query generates don't overlap with the links that the data center query above generates. Here are the links I get for the custom query:

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
  }
}

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

The challenge seems to be that the data center query returns links that aren't particularly informative once you already understand the shape of the data. For example, I already know that the region for data center 1 is at /datacenters/1/region, so if I want actual information about which specific region is involved, I have to follow the link to get it. In particular I have to follow the link to get the canonical URI that shows up in the bulk queries that would allow me to avoid n+1 queries.

推荐答案

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

The reason Spring Data REST works like this is the following: by default, we assume every application repository a primary resource of the REST service. Thus, if you expose a repository for an entity's related object you get links rendered to it and we expose the assignment of one entity to another via a nested resource (e.g. foo/{id}/bar).

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

To prevent this, annotate the related repository interface with @RestResource(exported = false) which prevents the entities managed by this repository from becoming top level resources.

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

The more general approach to this is starting with Spring Data REST letting you expose the resources you want to get managed and default rules applied. You can then customize the rendering and links by implementing ResourceProcessor<T> and registering your implementation as Spring bean. The ResourceProcessor will then allow you to customize the data rendered, links added to the representation etc.

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

For everything else, manually implement controllers (potentially blending into the URI space of the default controllers) and add links to those through ResourceProcessor implementations. An example for this can be seen in the Spring RESTBucks sample. The sample project uses Spring Data REST to manage Order instances and implements a custom controller to implement the more complex payment process. Beyond that it adds a link to the Order resource to point to the manually implemented code.

这篇关于如何避免使用 Spring Data Rest 进行 n+1 查询?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆