Как избежать n+1 запросов с помощью Spring Data Rest?

Вопрос. Как избежать n+1 запросов с Spring Data REST?

Фон. При запросе 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"
}

Однако довольно типично хотеть получить связанную информацию, не выполняя n+1 запросов. Чтобы придерживаться приведенного выше примера, я мог бы хотеть отобразить список центров обработки данных и связанных с ними областей в пользовательском интерфейсе.

Что я пробовал Я создал собственный запрос на моем RegionRepository чтобы получить все регионы для заданного набора ключей центра обработки данных:

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

К сожалению, ссылки, сгенерированные этим запросом, не пересекаются со ссылками, сгенерированными вышеупомянутым запросом центра обработки данных. Вот ссылки, которые я получаю для пользовательского запроса:

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 запросов.

2 ответа

Решение

Причина, по которой 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, чтобы указать на реализованный вручную код.

Spring Data REST создаст представление, которое вы описываете, только если сериализатор, настроенный внутри Jackson ObjectMapper, запущен, увидев PersistentEntityResource, который является особым видом Resource который используется внутри Spring Data REST.

Если вы создаете ResourceProcessor<Resource<MyPojo>> и вернуть new Resource<MyPojo>(origResource.getContent(), origResource.getLinks()), тогда механизм сериализации Spring Data REST по умолчанию не сработает, и будут применены обычные правила сериализации Джексона.

Обратите внимание, однако, что причина, по которой Spring Data REST связывается так, как это происходит, заключается в том, что очень трудно произвольно прекратить обход графа объектов при сериализации в JSON. Работая с ассоциациями так, как он это делает, он гарантирует, что сериализатор не начнет проходить граф объектов, который имеет N уровней глубины, и станет намного медленнее в производительности и производительности представления по проводам.

Обеспечение того, чтобы Джексон не пытался сериализовать PersistentEntityResourceТо, что он делает в конфигурации по умолчанию, гарантирует, что ни одна из обработок ассоциаций Spring Data REST не будет запущена. Недостатком этого является то, что ни один из помощников Spring Data REST не будет запущен. Если вам по-прежнему нужны ссылки на связанные ресурсы, вам необходимо убедиться, что вы их сами создали и добавили в исходный текст. Resource,

Другие вопросы по тегам