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

查看:160
本文介绍了如何避免使用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<T>并将实现注册为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实例并实现添加了链接到订单资源以指向手动实现的代码.

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天全站免登陆