Java での DynamoDB Query(AWS SDK v2)

AWS SDK for Java 2.x の queryPaginator はコレクションのように見えますが、コレクションではありません。遅延評価される再反復可能オブジェクトであり、その違いは 2 回目にループしたときに請求書に現れます。そもそも Query が正しい読み取りなのかどうかは Query と Scan の比較を参照してください。

コード

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 は反復のたびにクエリを実行し直します

1 曲あたり約 3.9 KB、すべてが Artist = "Arturo Sandoval" の下にある 600 曲のフィクスチャに対して、ddb.queryPaginator(request) は 2.6 ms で返り、何も送信しませんでした。そのうえで、同じ QueryIterable オブジェクトを 2 回ループしました。

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 object

1 つの変数に対する 2 つの for ループで、569 読み取りユニットです。ページはキャッシュされていません。反復のたびに LastEvaluatedKey を最初からたどり直します。アイテムが 2 回必要なら、例のように 1 度だけ List に汲み出してください。

同じ遅延評価の裏返しが、エラーが表面化する場所です。パーティションキーを欠いたキー条件のリクエストを組み立てても、まだ呼び出しが起きていないため queryPaginator は何も言わずに受け取ります。

queryPaginator(bad) constructed without throwing
threw on iteration: DynamoDbException / ValidationException /
Query condition missed key schema element / http 400

ビルダーを囲んだ try-catch は何も捕まえません。囲むべきはループのほうで、例がブロック全体を 1 つの try に入れているのはそのためです。

2026-07-28 に、OpenJDK 26.0.1 上の software.amazon.awssdk:dynamodb 2.49.4 で、DynamoDB Local(amazon/dynamodb-local)に対して計測しました。

解説

  • ddb.queryPaginator(request).items() はページを平坦化して Iterable<Map<String, AttributeValue>> にし、裏でページ送りするので、アイテムだけが欲しいときは例の二重ループが 1 つになります。SdkIterable でもあるので .stream() も使えます。
  • ビルダーでは数値は String ですAttributeValue.builder().n("1994").n(1994) のタイプミスではありません。n() のセッターは java.lang.String を取ります。DynamoDB がバイナリ浮動小数点の丸めを避けるために数値を 10 進テキストで運ぶからです。Java の int を渡してもコンパイルできません。
  • keyConditionExpression はパーティションキーの等価条件に加えて、ソートキーの条件を最大 1 つ取ります(=<<=>>=BETWEENbegins_with)。.scanIndexForward(false) で順序が逆になり、.indexName("...") でセカンダリインデックスに向けられます。
  • もう一方の使い勝手が Enhanced Client ですsoftware.amazon.awssdk.enhanced.dynamodbMap<String, AttributeValue> ではなくアノテーション付きの Bean をマッピングし、その query は上で計測したのと同じ遅延再反復の挙動を持つ PageIterable<T> を返します。

ビジュアルに行う

この例の #hashKey#rangeKey も、必須ではない別名です。ArtistSongTitle も AWS の 573 語の予約語リストには載っていません。予約語チェッカーは、自分の属性名のどれが本当に # 扱いを必要とするかを教えてくれるので、別名マップがカーゴカルトでなくなります。

QueryRequest を組み立てる前に、実際のテーブルに対してキー条件を試すには、DynoTable をダウンロードして結果をグリッドでページ送りしてください。

関連する例

参考資料

このリクエストをビジュアルに組み立てる

この操作を無料の DynamoDB クエリビルダーで組み立て — キー条件、フィルタ、インデックス、Limit、ソート順、ページネーションループ — 実行可能な SDK v3・CLI・boto3 のプログラムとしてコピーして戻れます。

DynamoDB クエリビルダーを開く

Console なしで DynamoDB を扱う

DynamoDB では実行できない本物の SQL(JOINs、GROUP BY、集計)を実行する高速な DynamoDB デスクトップクライアント。ビジュアル編集と、あなた自身の Bedrock キーで動く AI エージェントを備えています。

30日間無料トライアル、クレジットカード不要 — その後は期限のない Free プラン。