GraphQL-API-er med Spring Boot · leksjon

Implementering av bunting og hurtigbufring

Integrer DataLoaders i Spring Boot-resolverne dine for å optimalisere datahenting og redusere antallet rundturer til databasen.

Leksjon 3 av 411 trinn

Implementering av bunting og hurtigbufring er en gratis leksjon i GraphQL-API-er med Spring Boot på CoddyKit. Dette er leksjon 3 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i GraphQL-API-er med Spring Boot, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i GraphQL-API-er med Spring Boot inneholder totalt 4 leksjoner.

DataLoaders: På tide å implementere!

Velkommen tilbake! I tidligere leksjoner lærte vi om N+1-problemet og hvordan DataLoaders gir en elegant løsning gjennom samling i grupper og bufring.

I dag skal vi arbeide praktisk og integrere DataLoaders i en Spring Boot GraphQL-applikasjon. Vi skal se hvordan en BatchLoader defineres og registreres, og hvordan den brukes i GraphQL-resolverne.

Simulere en datatjeneste

Først setter vi opp en enkel mock-tjeneste som simulerer henting av brukere fra en database. Denne tjenesten vil bli kalt av DataLoaders.

Legg merke til System.out.println, som hjelper oss med å se når det faktiske «databasekallet» skjer, og som senere demonstrerer samling i grupper.

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

// A simple User data class
class User {
  String id;
  String name;
  User(String id, String name) { 
    this.id = id; this.name = name; 
  }
  public String getId() { return id; }
  public String getName() { return name; }
}

// Mock service to fetch users (simulates DB call)
class MockUserService {
  private final Map<String, User> users = Map.of(
    "1", new User("1", "Alice"),
    "2", new User("2", "Bob"),
    "3", new User("3", "Charlie"),
    "4", new User("4", "David")
  );

  public List<User> findAllByIds(List<String> ids) {
    System.out.println("DB Call: Fetching users for IDs: " + ids);
    return ids.stream()
              .map(users::get)
              .filter(user -> user != null)
              .collect(Collectors.toList());
  }
}

public class Main {
  public static void main(String[] args) {
    MockUserService service = new MockUserService();
    List<User> foundUsers = service.findAllByIds(List.of("1", "3"));
    System.out.println("Found: " + foundUsers.size() + " users.");
  }
}

BatchLoader-grensesnittet

Kjernen i DataLoaders mekanisme for samling i grupper er grensesnittet BatchLoader. Det definerer én metode: load(List<K> keys).

  • Den tar imot en liste med nøkler (for eksempel bruker-ID-er).
  • Den returnerer en CompletionStage (et fremtidig resultat) med en liste over verdier (for eksempel brukere).
  • Det avgjørende er at denne metoden kalles én gang for alle nøkler som etterspørres i løpet av et kort tidsvindu. Dermed kan de hentes i én optimalisert operasjon, for eksempel én databaseforespørsel.

Implementere Deres BatchLoader

Nå oppretter vi UserBatchLoader. Den implementerer grensesnittet BatchLoader<String, User>, noe som betyr at den tar imot en String (bruker-ID) som nøkkel og returnerer et User-objekt.

Den bruker MockUserService til å utføre selve batch-hentingen.

import org.dataloader.BatchLoader;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

// (Assume User and MockUserService are defined elsewhere)
// For runnable example, they would be in the same file.

public class UserBatchLoader implements BatchLoader<String, User> {
  private final MockUserService userService;

  public UserBatchLoader(MockUserService userService) {
    this.userService = userService;
  }

  @Override
  public CompletionStage<List<User>> load(List<String> userIds) {
    // This method is called once with all requested IDs.
    // We delegate to our mock service to fetch them in one go.
    return CompletableFuture.supplyAsync(
      () -> userService.findAllByIds(userIds)
    );
  }
}

public class Main {
  public static void main(String[] args) {
    MockUserService userService = new MockUserService();
    UserBatchLoader batchLoader = new UserBatchLoader(userService);
    System.out.println("UserBatchLoader created!");
  }
}

DataLoaderRegistry

I en Spring Boot GraphQL-applikasjon administreres DataLoader-instanser i en DataLoaderRegistry. Dette registeret fungerer som en beholder for alle DataLoaders som er tilgjengelige mens en GraphQL-forespørsel kjøres.

  • Hver DataLoader registreres med et unikt navn (for eksempel «userDataLoader»).
  • Resolverne henter den nødvendige DataLoader-en fra dette registeret ved hjelp av navnet.
  • Spring for GraphQL håndterer vanligvis livssyklusen og omfanget til dette registeret per forespørsel.

Konfigurere DataLoaders

Slik oppretter De en DataLoader-instans fra Deres BatchLoader og registrerer den i en DataLoaderRegistry. Dette oppsettet er avgjørende for å gjøre DataLoaders tilgjengelige for resolverne.

import org.dataloader.DataLoader;
import org.dataloader.DataLoaderRegistry;

// (Assume User, MockUserService, UserBatchLoader are defined)

public class Main {
  public static void main(String[] args) {
    MockUserService userService = new MockUserService();
    UserBatchLoader userBatchLoader = new UserBatchLoader(userService);

    // Create a DataLoader instance
    DataLoader<String, User> userDataLoader =
      DataLoader.newDataLoader(userBatchLoader);

    // Register it in a DataLoaderRegistry
    DataLoaderRegistry registry = new DataLoaderRegistry();
    registry.register("userDataLoader", userDataLoader);

    System.out.println("DataLoader registered as 'userDataLoader'!");
    // In a real app, this registry would be part of the GraphQLContext.
    // Resolvers would then retrieve DataLoaders from it.
  }
}

Bruke DataLoader i resolverne

Når DataLoader-en er registrert, kan De bruke den i GraphQL-resolverne. I stedet for å kalle tjenesten direkte kaller De dataLoader.load(id).

  • Metoden load() returnerer en CompletionStage, ikke selve objektet.
  • DataLoader samler alle load()-kall i den gjeldende kjøringsrammen før den sender dem videre til BatchLoader.
  • Spring for GraphQL sender automatisk DataLoaders videre på slutten av en datahentingssyklus.

Resolver-integrasjon i praksis

Dette eksempelet simulerer en GraphQL-forespørsel der flere bruker-ID-er etterspørres. Følg med på meldingen «DB Call» i resultatet for å se hvordan DataLoaders samler disse forespørslene i én enkelt operasjon, selv om de ser separate ut i resolver-logikken.

import org.dataloader.DataLoader;
import org.dataloader.DataLoaderRegistry;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;

// User, MockUserService, UserBatchLoader definitions (from previous scenes)
class User {
  String id; String name;
  User(String id, String name) { this.id = id; this.name = name; }
  public String getId() { return id; }
  public String getName() { return name; }
}

class MockUserService {
  private final Map<String, User> users = Map.of(
    "1", new User("1", "Alice"), "2", new User("2", "Bob"),
    "3", new User("3", "Charlie"), "4", new User("4", "David")
  );
  public List<User> findAllByIds(List<String> ids) {
    System.out.println("DB Call: Fetching users for IDs: " + ids);
    return ids.stream().map(users::get)
              .filter(user -> user != null).collect(Collectors.toList());
  }
}

class UserBatchLoader implements BatchLoader<String, User> {
  private final MockUserService userService;
  public UserBatchLoader(MockUserService userService) { this.userService = userService; }
  @Override
  public CompletionStage<List<User>> load(List<String> userIds) {
    return CompletableFuture.supplyAsync(() -> userService.findAllByIds(userIds));
  }
}

// Simulate a GraphQL Query Resolver method
public class UserResolver {
  private final DataLoaderRegistry dataLoaderRegistry;

  public UserResolver(DataLoaderRegistry registry) {
    this.dataLoaderRegistry = registry;
  }

  public CompletionStage<User> getUserById(String id) {
    DataLoader<String, User> userDataLoader =
      dataLoaderRegistry.getDataLoader("userDataLoader");
    return userDataLoader.load(id);
  }

  public static void main(String[] args) {
    MockUserService userService = new MockUserService();
    UserBatchLoader userBatchLoader = new UserBatchLoader(userService);
    DataLoader<String, User> userDataLoader =
      DataLoader.newDataLoader(userBatchLoader);

    DataLoaderRegistry registry = new DataLoaderRegistry();
    registry.register("userDataLoader", userDataLoader);

    UserResolver resolver = new UserResolver(registry);

    System.out.println("Requesting User 1 and User 2 concurrently...");

    // Simulate two concurrent GraphQL field fetches
    CompletableFuture<User> user1Future =
      (CompletableFuture<User>) resolver.getUserById("1");
    CompletableFuture<User> user2Future =
      (CompletableFuture<User>) resolver.getUserById("2");

    CompletableFuture.allOf(user1Future, user2Future)
      .thenRun(() -> {
        try {
          System.out.println("Fetched User 1: " + user1Future.join().getName());
          System.out.println("Fetched User 2: " + user2Future.join().getName());
        } catch (Exception e) {
          e.printStackTrace();
        }
        // For this manual demo, we dispatch the DataLoader.
        // In Spring GraphQL, this is handled automatically.
        userDataLoader.dispatch();
      }).join();
  }
}

DataLoaders innebygde bufring

I tillegg til samling i grupper tilbyr DataLoaders også bufring per forespørsel direkte. Det betyr følgende:

  • Hvis dataLoader.load("1") kalles flere ganger i løpet av den samme GraphQL-forespørselen, blir BatchLoader-ens hente-funksjon for ID «1» bare kalt én gang.
  • Senere kall for samme ID i løpet av denne forespørselen returnerer resultatet som allerede er hentet og bufret.
  • Dette forhindrer overflødige databasekall for de samme dataene i én GraphQL-operasjon og gir ytterligere bedre ytelse.

Kontroller forståelsen Deres

De har lært hvordan DataLoaders implementeres for samling i grupper og bufring. La oss teste kunnskapene Deres!

Oppsummering: Samling og bufring

Godt jobbet! De har nå lært hvordan DataLoaders implementeres i Spring Boot GraphQL.

  • Vi opprettet en MockUserService for å simulere datahenting.
  • Vi bygde en UserBatchLoader for å håndtere samling av flere ID-forespørsler.
  • Vi så hvordan DataLoader-instanser initialiseres og registreres i en DataLoaderRegistry.
  • Til slutt integrerte vi DataLoaders i en resolver og så hvordan forespørsler samles, og hvordan bufring forhindrer overflødige kall.

Ved å bruke DataLoaders optimaliserer De ytelsen til GraphQL API-et betydelig, særlig ved arbeid med komplekse datagrafer og nestede objekter.

Gratis å komme i gang

Lær deg GraphQL-API-er med Spring Boot med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
12
Leksjoner
48

Ofte stilte spørsmål

Er leksjonen «Implementering av bunting og hurtigbufring» gratis?

Ja – hele teksten i «Implementering av bunting og hurtigbufring» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av GraphQL-API-er med Spring Boot-kurset, kan du oppgradere til CoddyKit PRO. Kurset i GraphQL-API-er med Spring Boot inneholder totalt 4 leksjoner.

Hva lærer jeg i «Implementering av bunting og hurtigbufring»?

Integrer DataLoaders i Spring Boot-resolverne dine for å optimalisere datahenting og redusere antallet rundturer til databasen. Du øver på GraphQL-API-er med Spring Boot med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med GraphQL-API-er med Spring Boot?

Ingen tidligere erfaring er nødvendig. GraphQL-API-er med Spring Boot på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Implementering av bunting og hurtigbufring»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne GraphQL-API-er med Spring Boot-leksjonen?

Ja. Alle GraphQL-API-er med Spring Boot-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. N+1-problemet forklart
  2. Introduksjon til GraphQL DataLoaders
  3. Implementering av bunting og hurtigbufring
  4. DataLoaders med Spring Context og asynkronitet
← Tilbake til GraphQL-API-er med Spring Boot