DynamoDB Query en Java (AWS SDK v2)
queryPaginator dans AWS SDK for Java 2.x ressemble à une collection et n'en est pas une. C'est un itérable paresseux réutilisable, et la différence se voit sur ta facture la deuxième fois que tu boucles dessus. Pour savoir si Query est la bonne lecture tout court, voir Query vs. Scan.
Code
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.QueryRequest;
import software.amazon.awssdk.services.dynamodb.model.QueryResponse;
public class QueryExample {
public static void main(String[] args) {
try (DynamoDbClient ddb = DynamoDbClient.builder()
.region(Region.US_EAST_1)
.build()) {
Map<String, String> names = new HashMap<>();
names.put("#hashKey", "Artist");
names.put("#rangeKey", "SongTitle");
Map<String, AttributeValue> values = new HashMap<>();
values.put(":hashKeyValue", AttributeValue.builder().s("Arturo Sandoval").build());
values.put(":rangeKeyValue", AttributeValue.builder().s("C").build());
QueryRequest request = QueryRequest.builder()
.tableName("Music")
.keyConditionExpression(
"#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)")
.expressionAttributeNames(names)
.expressionAttributeValues(values)
.build();
List<Map<String, AttributeValue>> items = new ArrayList<>();
for (QueryResponse page : ddb.queryPaginator(request)) {
items.addAll(page.items());
}
System.out.println("Found " + items.size() + " items");
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
}
}
}QueryIterable relance la requête à chaque itération
Sur un jeu d'essai de 600 morceaux, chacun d'environ 3,9 Ko et tous sous Artist = "Arturo Sandoval", ddb.queryPaginator(request) a rendu la main en 2,6 ms sans rien envoyer. Puis le même objet QueryIterable a été parcouru deux fois :
queryPaginator(request) returned in 2.60 ms (class QueryIterable, 0 requests)
iteration 1: pages=3 items=600 capacity=284.5
iteration 2: pages=3 items=600 capacity=284.5 <- same objectDeux boucles for sur une seule variable, 569 unités de lecture. Les pages n'ont jamais été mises en cache ; chaque itération reparcourt LastEvaluatedKey depuis le début. Si tu as besoin des éléments deux fois, vide l'itérable une fois dans une List, comme le fait l'exemple.
L'autre face de cette même paresse, c'est l'endroit où les erreurs remontent. Construis une requête dont la condition de clé omet la clé de partition et queryPaginator l'accepte sans broncher, parce qu'aucun appel n'a encore eu lieu :
queryPaginator(bad) constructed without throwing
threw on iteration: DynamoDbException / ValidationException /
Query condition missed key schema element / http 400Un try-catch enroulé autour du builder n'attrape rien. Il doit entourer la boucle, et c'est pour ça que l'exemple met tout le bloc dans un seul try.
Mesuré le 2026-07-28 contre DynamoDB Local (amazon/dynamodb-local) avec software.amazon.awssdk:dynamodb 2.49.4 sur OpenJDK 26.0.1.
Explication
ddb.queryPaginator(request).items()aplatit les pages en unIterable<Map<String, AttributeValue>>et pagine derrière toi, si bien que la double boucle de l'exemple se réduit à une seule quand tu ne veux que les éléments. C'est aussi unSdkIterable, donc.stream()fonctionne.- Les nombres sont des
Stringdans le builder.AttributeValue.builder().n("1994")n'est pas une faute de frappe pour.n(1994)— le settern()prend unjava.lang.String, parce que DynamoDB transporte les nombres en texte décimal pour éviter les arrondis des flottants binaires. Passer unintJava ne compilera pas. keyConditionExpressionprend une égalité sur la clé de partition plus au plus une condition sur la clé de tri (=,<,<=,>,>=,BETWEEN,begins_with) ;.scanIndexForward(false)inverse l'ordre et.indexName("...")recible un index secondaire.- L'Enhanced Client est l'autre ergonomie.
software.amazon.awssdk.enhanced.dynamodbmappe des beans annotés au lieu deMap<String, AttributeValue>, et sa méthodequeryrenvoie unPageIterable<T>avec la même sémantique de réitération paresseuse mesurée plus haut.
Le faire visuellement
#hashKey et #rangeKey sont ici des alias que rien ne t'impose : ni Artist ni SongTitle ne figurent sur la liste AWS des 573 mots réservés. Le vérificateur de mots réservés te dit lesquels de tes noms d'attribut ont vraiment besoin du traitement #, pour que la map d'alias cesse d'être un rituel.
Pour essayer une condition de clé sur une vraie table avant de construire le QueryRequest, télécharge DynoTable et parcours les résultats dans une grille.
Exemples liés
- DynamoDB Query en Go — la même requête avec AWS SDK for Go v2.
- DynamoDB Scan en Java — quand tu ne peux pas cibler une partition par la clé.
- Query vs. Scan — pourquoi
Queryest le bon choix par défaut. - Les expressions de condition de clé — tous les opérateurs légaux sur clé de partition/de tri.
- "Query condition missed key schema element" — la condition de clé nomme le mauvais attribut ou saute la clé de partition.
- "Query key condition not supported" — un opérateur que la condition de clé ne peut pas utiliser, comme contains ou une deuxième condition sur la clé de tri.