query projections example data custom spring rest spring-data spring-data-rest spring-hateoas

projections - ¿Cómo evito las consultas n+1 con Spring Data Rest?



spring data rest base path (2)

Pregunta. ¿Cómo evito las consultas n + 1 con Spring Data REST?

Fondo. Al consultar Spring Data REST para obtener una lista de recursos, cada uno de los recursos de nivel superior resultantes tiene enlaces a los recursos asociados, en lugar de tener los recursos asociados incrustados directamente en los recursos de nivel superior. Por ejemplo, si busco una lista de centros de datos, las regiones asociadas aparecen como enlaces, como este:

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

Sin embargo, es bastante típico querer obtener la información asociada sin tener que hacer n + 1 consultas. Para seguir con el ejemplo anterior, es posible que desee mostrar una lista de centros de datos y sus regiones asociadas en una interfaz de usuario.

Lo que he intentado. Creé una consulta personalizada en mi RegionRepository para obtener todas las regiones para un conjunto dado de claves de centros de datos:

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

Desafortunadamente, los enlaces que genera esta consulta no se superponen con los enlaces que genera la consulta del centro de datos anterior. Aquí están los enlaces que obtengo para la consulta personalizada:

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

El desafío parece ser que la consulta del centro de datos devuelve enlaces que no son particularmente informativos una vez que ya comprende la forma de los datos. Por ejemplo, ya sé que la región para el centro de datos 1 está en /datacenters/1/region , así que si quiero información real sobre qué región específica está involucrada, tengo que seguir el enlace para obtenerla. En particular, debo seguir el enlace para obtener el URI canónico que aparece en las consultas masivas que me permitirían evitar las consultas n + 1.


La razón por la que Spring Data REST funciona de esta manera es la siguiente: de manera predeterminada, asumimos que cada repositorio de aplicaciones es un recurso primario del servicio REST. Por lo tanto, si expone un repositorio para el objeto relacionado de una entidad, obtiene los enlaces que se le representan y nosotros exponemos la asignación de una entidad a otra a través de un recurso anidado (por ejemplo, foo/{id}/bar ).

Para evitar esto, anote la interfaz del repositorio relacionada con @RestResource(exported = false) que evita que las entidades administradas por este repositorio se conviertan en recursos de nivel superior.

El enfoque más general de esto es comenzar con Spring Data REST, que le permite exponer los recursos que desea administrar y aplicar las reglas predeterminadas. Luego, puede personalizar la representación y los enlaces implementando ResourceProcessor<T> y registrando su implementación como Spring Bean. El ResourceProcessor le permitirá personalizar los datos representados, los enlaces agregados a la representación, etc.

Para todo lo demás, implementar controladores de forma manual (potencialmente mezclando en el espacio URI de los controladores predeterminados) y agregar enlaces a ellos a través de las implementaciones del ResourceProcessor . Un ejemplo de esto se puede ver en la muestra de RESTBucks de Spring . El proyecto de ejemplo utiliza Spring Data REST para administrar las instancias de Order e implementa un controlador personalizado para implementar el proceso de pago más complejo. Más allá de eso, agrega un enlace al recurso de Orden para apuntar al código implementado manualmente.


Spring Data REST solo creará la representación que usted describe si el serializador que está configurado dentro del Jackson ObjectMapper se activa al ver un PersistentEntityResource , que es un tipo especial de Resource que se usa dentro de Spring Data REST.

Si crea un ResourceProcessor<Resource<MyPojo>> y devuelve un new Resource<MyPojo>(origResource.getContent(), origResource.getLinks()) , la maquinaria de serialización Spring Data REST predeterminada no se activará y las reglas de serialización normales de Jackson se aplicará

Tenga en cuenta, sin embargo, que la razón por la que Spring Data REST hace asociaciones de la forma en que lo hace es porque es muy difícil detener arbitrariamente un gráfico de objetos cuando se serializa a JSON. Al manejar las asociaciones de la forma en que lo hace, garantiza que el serializador no comience a recorrer un gráfico de objetos con N niveles profundos y que se vuelva mucho más lento en el rendimiento y en el rendimiento de la representación a través del cable.

Asegurarse de que Jackson no intente serializar un objeto PersistentEntityResource , que es lo que está haciendo en la configuración predeterminada, se asegurará de que no se active ningún manejo de asociaciones REST de Spring Data. El aspecto negativo de esto, por supuesto, es que ninguno de los ayudantes de Spring Data REST se activará. Si aún desea enlaces a los recursos asociados, deberá asegurarse de crearlos usted mismo y agregarlos al Resource simple saliente.