Query do DynamoDB em Java (AWS SDK v2)
O queryPaginator do AWS SDK for Java 2.x parece uma coleção e não é uma. Ele é um re-iterável preguiçoso, e a diferença aparece na sua conta na segunda vez que você percorre o laço. Para saber quando o Query é a leitura certa, veja Query vs. Scan.
Código
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());
}
}
}O QueryIterable re-executa a consulta a cada iteração
Contra um fixture de 600 músicas, cada uma com ~3,9 KB e todas sob Artist = "Arturo Sandoval", o ddb.queryPaginator(request) retornou em 2,6 ms e não enviou nada. Depois o mesmo objeto QueryIterable foi percorrido duas vezes:
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 objectDois laços for sobre uma variável, 569 unidades de leitura. As páginas nunca foram cacheadas; cada iteração percorre o LastEvaluatedKey desde o começo de novo. Se você precisa dos itens duas vezes, drene o iterável para uma List uma vez, como o exemplo faz.
O outro lado dessa mesma preguiça é onde os erros aparecem. Construa uma requisição cuja condição de chave omita a chave de partição e o queryPaginator a aceita sem reclamar, porque nenhuma chamada aconteceu ainda:
queryPaginator(bad) constructed without throwing
threw on iteration: DynamoDbException / ValidationException /
Query condition missed key schema element / http 400Um try-catch em volta do builder não captura nada. Ele precisa envolver o laço, e é por isso que o exemplo coloca o bloco inteiro dentro de um único try.
Medido em 2026-07-28 contra o DynamoDB Local (amazon/dynamodb-local) com software.amazon.awssdk:dynamodb 2.49.4 no OpenJDK 26.0.1.
Explicação
ddb.queryPaginator(request).items()achata as páginas em umIterable<Map<String, AttributeValue>>e pagina por baixo dos panos, então o laço de dois níveis do exemplo vira um só quando você quer apenas os itens. Ele também é umSdkIterable, então.stream()funciona.- Números são
Stringno builder.AttributeValue.builder().n("1994")não é um erro de digitação de.n(1994)— o settern()recebe umjava.lang.String, porque o DynamoDB transporta números como texto decimal para evitar arredondamento binário de ponto flutuante. Passar umintdo Java não compila. keyConditionExpressionrecebe uma igualdade na chave de partição mais no máximo uma condição de chave de classificação (=,<,<=,>,>=,BETWEEN,begins_with);.scanIndexForward(false)inverte a ordem e.indexName("...")redireciona para um índice secundário.- O Enhanced Client é a outra ergonomia. O
software.amazon.awssdk.enhanced.dynamodbmapeia beans anotados em vez deMap<String, AttributeValue>, e oquerydele retorna umPageIterable<T>com a mesma semântica de re-iteração preguiçosa medida acima.
Faça isso visualmente
Tanto #hashKey quanto #rangeKey neste exemplo são aliases que nada obriga você a usar: nem Artist nem SongTitle estão na lista de 573 palavras reservadas da AWS. O verificador de palavras reservadas te diz quais dos seus nomes de atributo de fato precisam do tratamento com #, para o mapa de aliases deixar de ser culto à carga.
Para experimentar uma condição de chave contra uma tabela real antes de construir o QueryRequest, baixe o DynoTable e navegue pelos resultados em uma grade.
Exemplos relacionados
- Query do DynamoDB em Go — a mesma consulta com o AWS SDK for Go v2.
- Scan do DynamoDB em Java — quando você não consegue entrar por uma partição.
- Query vs. Scan — por que o
Queryé o padrão certo. - Expressões de condição de chave — todos os operadores legais de chave de partição/classificação.
- "Query condition missed key schema element" — a condição de chave nomeia o atributo errado ou pula a chave de partição.
- "Query key condition not supported" — um operador que a condição de chave não pode usar, como contains ou uma segunda condição de chave de classificação.