Skip to content

Commit bde86d3

Browse files
committed
Update docs on batch loading registration and tests
Closes gh-246
1 parent bc70922 commit bde86d3

File tree

1 file changed

+102
-41
lines changed

1 file changed

+102
-41
lines changed

spring-graphql-docs/src/docs/asciidoc/index.adoc

Lines changed: 102 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -346,44 +346,108 @@ default it is marked as `INTERNAL_ERROR`.
346346

347347

348348
[[execution-batching]]
349-
=== Batching
350-
351-
Given a `Book` and its `Author`, we can create one `DataFetcher` for books and another
352-
for the author of a book. This means books and authors aren't automatically loaded
353-
together, which enables queries to select the subset of data they need. However, loading
354-
multiple books, results in loading each author individually, and this is a performance
355-
issue known as the N+1 select problem.
356-
357-
GraphQL Java provides a
358-
https://www.graphql-java.com/documentation/v16/batching/[batching feature] that allows
359-
related entities, such as the authors for all books, to be loaded together. This is how
360-
the underlying mechanism works in GraphQL Java:
361-
362-
- For each request, an application can register a batch loading function as a
363-
`DataLoader` in the `DataLoaderRegistry` to assist with loading instances of a given
364-
entity, such as `Author` from a set of unique keys.
365-
- A `DataFetcher` can access the `DataLoader` for the entity and use it to load entity
366-
instances; for example the author `DataFetcher` obtains the authorId from the `Book`
367-
parent object, and uses it to load the `Author`.
368-
- `DataLoader` does not load the entity immediately but rather returns a future, and
369-
defers until it is ready to batch load all related entities as one.
370-
- `DataLoader` additionally maintains a cache of previously loaded entities that can
371-
further improve efficiency when the same entity is in multiple places of the response.
372-
373-
Spring for GraphQL provides:
374-
375-
- `BatchLoaderRegistry` that accepts and stores registrations of batch loading functions;
376-
This is used in `ExecutionGraphQlService` to make `DataLoader` registrations per request.
377-
- <<controllers-schema-mapping-data-loader,DataLoader argument>> for `@SchemaMapping`
378-
methods to access the `DataLoader` for the field type.
379-
- <<controllers-batch-mapping,@BatchMapping>> data controller methods that provide a
380-
shortcut and avoid the need to use `DataLoader` directly.
349+
=== Batch Loading
350+
351+
Given a `Book` and its `Author`, we can create one `DataFetcher` for a book and another
352+
for its author. This allows selecting books with or without authors, but it means books
353+
and authors aren't loaded together, which is especially inefficient when querying multiple
354+
books as the author for each book is loaded individually. This is known as the N+1 select
355+
problem.
356+
357+
358+
[[execution-batching-dataloader]]
359+
==== `DataLoader`
360+
361+
GraphQL Java provides a `DataLoader` mechanism for batch loading of related entities.
362+
You can find the full details in the
363+
https://www.graphql-java.com/documentation/v16/batching/[GraphQL Java docs]. Below is a
364+
summary of how it works:
365+
366+
1. Register ``DataLoader``'s in the `DataLoaderRegistry` that can load entities, given unique keys.
367+
2. ``DataFetcher``'s can access ``DataLoader``'s and use them to load entities by id.
368+
3. A `DataLoader` defers loading by returning a future so it can be done in a batch.
369+
4. ``DataLoader``'s maintain a per request cache of loaded entities that can further
370+
improve efficiency.
371+
372+
373+
[[execution-batching-batch-loader-registry]]
374+
==== `BatchLoaderRegistry`
375+
376+
The complete batching loading mechanism in GraphQL Java requires implementing one of
377+
several `BatchLoader` interface, then wrapping and registering those as ``DataLoader``s
378+
with a name in the `DataLoaderRegistry`.
379+
380+
The API in Spring GraphQL is slightly different. For registration, there is only one,
381+
central `BatchLoaderRegistry` exposing factory methods and a builder to create and
382+
register any number of batch loading functions:
383+
384+
[source,java,indent=0,subs="verbatim,quotes"]
385+
----
386+
@Configuration
387+
public class MyConfig {
388+
389+
public MyConfig(BatchLoaderRegistry registry) {
390+
391+
registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> {
392+
// return Mono<Map<Long, Author>
393+
});
394+
395+
// more registrations ...
396+
}
397+
398+
}
399+
----
381400

382401
The Spring Boot starter declares a
383-
<<boot-graphql-batch-loader-registry,BatchLoaderRegistry bean>>, so that applications can
384-
simply autowire the registry into their controllers in order to register batch loading
385-
functions for entities.
402+
<<boot-graphql-batch-loader-registry,BatchLoaderRegistry bean>> so you can inject it into
403+
your configuration, as shown above, or into any component such as a controller in order
404+
register batch loading functions. In turn the `BatchLoaderRegistry` is injected into
405+
`ExecutionGraphQlService` where it ensures `DataLoader` registrations per request.
406+
407+
By default, the `DataLoader` name is based on the class name of the target entity.
408+
This allows an `@SchemaMapping` method to declare a
409+
<<controllers-schema-mapping-data-loader,DataLoader argument>> with a generic type, and
410+
without the need for specifying a name. The name, however, can be customized through the
411+
`BatchLoaderRegistry` builder, if necessary, along with other `DataLoader` options.
412+
413+
For many cases, when loading related entities, you can use
414+
<<controllers-batch-mapping,@BatchMapping>> controller methods, which are a shortcut
415+
for and replace the need to use `BatchLoaderRegistry` and `DataLoader` directly.
416+
s
417+
`BatchLoaderRegistry` provides other important benefits too. It supports access to
418+
the same `GraphQLContext` from batch loading functions and from `@BatchMapping` methods,
419+
as well as ensures <<execution-context>> to them. This is why applications are expected
420+
to use it. It is possible to perform your own `DataLoader` registrations directly but
421+
such registrations would forgo the above benefits.
422+
423+
424+
[[execution-batching-testing]]
425+
==== Testing Batch Loading
426+
427+
Start by having `BatchLoaderRegistry` perform registrations on a `DataLoaderRegistry`:
428+
429+
[source,java,indent=0,subs="verbatim,quotes"]
430+
----
431+
BatchLoaderRegistry batchLoaderRegistry = new DefaultBatchLoaderRegistry();
432+
// perform registrations...
386433
434+
DataLoaderRegistry dataLoaderRegistry = DataLoaderRegistry.newRegistry().build();
435+
batchLoaderRegistry.registerDataLoaders(dataLoaderRegistry, graphQLContext);
436+
----
437+
438+
Now you can access and test individual ``DataLoader``'s as follows:
439+
440+
[source,java,indent=0,subs="verbatim,quotes"]
441+
----
442+
DataLoader<Long, Book> loader = dataLoaderRegistry.getDataLoader(Book.class.getName());
443+
loader.load(1L);
444+
loader.loadMany(Arrays.asList(2L, 3L));
445+
List<Book> books = loader.dispatchAndJoin(); // actual loading
446+
447+
assertThat(books).hasSize(3);
448+
assertThat(books.get(0).getName()).isEqualTo("...");
449+
// ...
450+
----
387451

388452

389453

@@ -987,12 +1051,9 @@ to locate it in the `DataLoaderRegistry`. As a fallback, the `DataLoader` method
9871051
resolver will also try the method argument name as the key but typically that should not
9881052
be necessary.
9891053

990-
[TIP]
991-
====
992-
For straight-forward cases where the `@SchemaMapping` simply delegates to a `DataLoader`,
993-
you can reduce boilerplate by using a <<controllers-batch-mapping,@BatchMapping>> method
994-
instead.
995-
====
1054+
Note that for many cases with loading related entities, where the `@SchemaMapping` simply
1055+
delegates to a `DataLoader`, you can reduce boilerplate by using a
1056+
<<controllers-batch-mapping,@BatchMapping>> method as described in the next section.
9961057

9971058

9981059

0 commit comments

Comments
 (0)