Qtの応用 - AWS DynamoDB

提供: MochiuWiki : SUSE, EC, PCB

📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

概要

AWS DynamoDBは、AWSが提供するフルマネージド型のNoSQLデータベースサービスである。
キー・バリュー型およびドキュメント型のデータモデルをサポートし、高速で予測可能なパフォーマンスとシームレスなスケーラビリティを実現する。

主な特徴を以下に示す。

  • フルマネージド
    サーバ管理、ソフトウェアパッチ、セットアップ不要
  • 高パフォーマンス
    一桁ミリ秒のレスポンスタイム
  • 自動スケーリング
    トラフィックに応じて自動的に容量を調整
  • 高可用性
    複数のAZに自動的にデータを複製
  • 柔軟なスキーマ
    各アイテムが異なる属性を持つことが可能


AWS DynamoDBのデータモデルを以下に示す。

  • テーブル
    データを格納する最上位のコンテナ
  • アイテム
    テーブル内の個々のレコード
  • 属性
    アイテムを構成するデータフィールド (カラムに相当)
  • プライマリキー
    パーティションキー (必須) と ソートキー (オプション) で構成


AWS DynamoDBは永続的な無料利用枠を提供しており、個人利用や小規模なアプリケーションであれば無料枠内で運用できることが多い。


AWS SDK for C++のインストール

Qtの応用_-_AWS#AWS_SDK_for_C++のインストールのページを参照すること。


1件のデータ挿入 : PutItem

テーブルに新しいアイテムを追加する。

 // アイテムを挿入
 Aws::DynamoDB::Model::PutItemRequest putReq;
 putReq.SetTableName(tableName);
 putReq.AddItem("id", toAttr(1));
 putReq.AddItem("name", toAttr("Taro Yamada"));
 putReq.AddItem("age", toAttr(30));
 putReq.AddItem("email", toAttr("taro@example.com"));
 
 auto putResult = client.PutItem(putReq);
 if (putResult.IsSuccess()) {
    qDebug() << "✓ Item inserted successfully";
 }
 else {
    qDebug() << "✗ Error: " << QString::fromStdString(putResult.GetError().GetMessage());
 }



1件のデータ取得 :GetItem

プライマリキーを指定して1件のアイテムを取得する。

 // プライマリキーでアイテムを取得
 Aws::DynamoDB::Model::GetItemRequest getReq;
 getReq.SetTableName(tableName);
 getReq.AddKey("id", toAttr(1));
 
 auto getResult = client.GetItem(getReq);
 if (getResult.IsSuccess()) {
    const auto& item = getResult.GetResult().GetItem();
    if (!item.empty()) {
       printItem(item);
    }
    else {
       qDebug() << "Item not found";
    }
 }
 else {
    qDebug() << "✗ Error: " << QString::fromStdString(getResult.GetError().GetMessage());
 }



複数件のデータ取得 : Query

パーティションキーを条件として複数件のアイテムを取得する。

 // クエリで複数件取得 (パーティションキーでフィルタ)
 Aws::DynamoDB::Model::QueryRequest queryReq;
 queryReq.SetTableName(tableName);
 queryReq.SetKeyConditionExpression("id = :value");
 queryReq.AddExpressionAttributeValues(":value", toAttr(1));
 
 // オプション : 取得件数制限
 queryReq.SetLimit(10);
 
 auto queryResult = client.Query(queryReq);
 if (queryResult.IsSuccess()) {
    const auto& items = queryResult.GetResult().GetItems();
    qDebug() << "Found" << items.size() << "items";
 
    for (const auto& item : items) {
       printItem(item);
    }
 }
 else {
    qDebug() << "✗ Error: " << QString::fromStdString(queryResult.GetError().GetMessage());
 }



ソートキー条件を含むクエリ

パーティションキーとソートキーを組み合わせた条件でデータを取得する。

 // パーティションキーとソートキーでクエリ
 Aws::DynamoDB::Model::QueryRequest queryReq;
 queryReq.SetTableName(tableName);
 
 // 例: userId = 100 かつ timestamp > 1234567890
 queryReq.SetKeyConditionExpression("userId = :uid AND #ts > :time");
 
 // 属性名のエイリアス(予約語の場合に使用)
 queryReq.AddExpressionAttributeNames("#ts", "timestamp");
 
 // 属性値
 queryReq.AddExpressionAttributeValues(":uid", toAttr(100));
 queryReq.AddExpressionAttributeValues(":time", toAttr(1234567890));
 
 auto queryResult = client.Query(queryReq);
 if (queryResult.IsSuccess()) {
    const auto& items = queryResult.GetResult().GetItems();
    qDebug() << "Found" << items.size() << "items";
 
    for (const auto& item : items) {
       printItem(item);
    }
 }



全件取得 : Scan

テーブル内の全アイテムをスキャンして取得する。

Scanは全テーブルスキャンのため、大量データには使用しない。
Queryを優先すること。

 // テーブル全体をスキャン
 Aws::DynamoDB::Model::ScanRequest scanReq;
 scanReq.SetTableName(tableName);
 
 // オプション: 取得件数制限
 scanReq.SetLimit(100);
 
 auto scanResult = client.Scan(scanReq);
 if (scanResult.IsSuccess()) {
    const auto& items = scanResult.GetResult().GetItems();
    qDebug() << "Found" << items.size() << "items";
 
    for (const auto& item : items) {
       printItem(item);
    }
 }
 else {
    qDebug() << "✗ Error: " << QString::fromStdString(scanResult.GetError().GetMessage());
 }



フィルタ条件付きスキャン

スキャン時に特定の条件でフィルタリングする。

 // 条件付きスキャン(例: age > 25)
 Aws::DynamoDB::Model::ScanRequest scanReq;
 scanReq.SetTableName(tableName);
 scanReq.SetFilterExpression("age > :minAge");
 scanReq.AddExpressionAttributeValues(":minAge", toAttr(25));
 
 auto scanResult = client.Scan(scanReq);
 if (scanResult.IsSuccess()) {
    const auto& items = scanResult.GetResult().GetItems();
    qDebug() << "Found" << items.size() << "items with age > 25";
 
    for (const auto& item : items) {
       printItem(item);
    }
 }



データの更新 : UpdateItem

既存のアイテムの属性を更新する。

 // アイテムを更新
 Aws::DynamoDB::Model::UpdateItemRequest updateReq;
 updateReq.SetTableName(tableName);
 updateReq.AddKey("id", toAttr(1));
 
 // 更新式: SET age = 31, email = "new@example.com"
 updateReq.SetUpdateExpression("SET age = :newAge, email = :newEmail");
 updateReq.AddExpressionAttributeValues(":newAge", toAttr(31));
 updateReq.AddExpressionAttributeValues(":newEmail", toAttr("new@example.com"));
 
 auto updateResult = client.UpdateItem(updateReq);
 if (updateResult.IsSuccess()) {
    qDebug() << "✓ Item updated successfully";
 }
 else {
    qDebug() << "✗ Error: " << QString::fromStdString(updateResult.GetError().GetMessage());
 }



条件付き更新

特定の条件を満たす場合のみ更新を実行する。

 // 条件付き更新(例: ageが30の場合のみ更新)
 Aws::DynamoDB::Model::UpdateItemRequest updateReq;
 updateReq.SetTableName(tableName);
 updateReq.AddKey("id", toAttr(1));
 
 updateReq.SetUpdateExpression("SET age = :newAge");
 updateReq.AddExpressionAttributeValues(":newAge", toAttr(31));
 
 // 条件式
 updateReq.SetConditionExpression("age = :currentAge");
 updateReq.AddExpressionAttributeValues(":currentAge", toAttr(30));
 
 auto updateResult = client.UpdateItem(updateReq);
 if (updateResult.IsSuccess()) {
    qDebug() << "✓ Conditional update succeeded";
 }
 else {
    if (updateResult.GetError().GetErrorType() == Aws::DynamoDB::DynamoDBErrors::CONDITIONAL_CHECK_FAILED) {
       qDebug() << "Condition not met - update skipped";
    }
    else {
       qDebug() << "✗ Error: " << QString::fromStdString(updateResult.GetError().GetMessage());
    }
 }



データの削除 : DeleteItem

プライマリキーを指定してアイテムを削除する。

 // アイテムを削除
 Aws::DynamoDB::Model::DeleteItemRequest delReq;
 delReq.SetTableName(tableName);
 delReq.AddKey("id", toAttr(1));
 
 auto delResult = client.DeleteItem(delReq);
 if (delResult.IsSuccess()) {
    qDebug() << "✓ Item deleted successfully";
 }
 else {
    qDebug() << "✗ Error: " << QString::fromStdString(delResult.GetError().GetMessage());
 }



条件付き削除

特定の条件を満たす場合のみ削除を実行する。

 // 条件付き削除
 // 例: statusが"inactive"の場合のみ削除
 Aws::DynamoDB::Model::DeleteItemRequest delReq;
 delReq.SetTableName(tableName);
 delReq.AddKey("id", toAttr(1));
 
 delReq.SetConditionExpression("#status = :statusValue");
 delReq.AddExpressionAttributeNames("#status", "status");
 delReq.AddExpressionAttributeValues(":statusValue", toAttr("inactive"));
 
 auto delResult = client.DeleteItem(delReq);
 if (delResult.IsSuccess()) {
    qDebug() << "✓ Conditional delete succeeded";
 }
 else {
    if (delResult.GetError().GetErrorType() == 
       Aws::DynamoDB::DynamoDBErrors::CONDITIONAL_CHECK_FAILED) {
       qDebug() << "Condition not met - delete skipped";
    }
 }



バッチ書き込み : BatchWriteItem

複数のアイテムを1度に挿入または削除する。
BatchWriteItemは最大25件まで1度に処理可能である。

 #include <aws/dynamodb/model/BatchWriteItemRequest.h>
 #include <aws/dynamodb/model/WriteRequest.h>
 
 // バッチ書き込み
 Aws::DynamoDB::Model::BatchWriteItemRequest batchReq;
 
 // 複数のPutRequestを作成
 std::vector<Aws::DynamoDB::Model::WriteRequest> writeRequests;
 
 for (int i = 1; i <= 5; i++) {
    Aws::DynamoDB::Model::PutRequest putRequest;
    Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue> item;
    item["id"] = toAttr(i);
    item["name"] = toAttr(QString("User %1").arg(i));
    item["age"] = toAttr(20 + i);
    putRequest.SetItem(item);
 
    Aws::DynamoDB::Model::WriteRequest writeReq;
    writeReq.SetPutRequest(putRequest);
    writeRequests.push_back(writeReq);
 }
 
 batchReq.AddRequestItems(tableName, writeRequests);
 
 auto batchResult = client.BatchWriteItem(batchReq);
 if (batchResult.IsSuccess()) {
    qDebug() << "✓ Batch write completed";
 
    // 未処理のアイテムがあるか確認
    const auto& unprocessed = batchResult.GetResult().GetUnprocessedItems();
    if (!unprocessed.empty()) {
       qDebug() << "Warning: Some items were not processed";
    }
 }
 else {
    qDebug() << "✗ Error: " << QString::fromStdString(batchResult.GetError().GetMessage());
 }



バッチ読み込み : BatchGetItem

複数のアイテムを1度に取得する。

 #include <aws/dynamodb/model/BatchGetItemRequest.h>
 #include <aws/dynamodb/model/KeysAndAttributes.h>
 
 // バッチ取得
 Aws::DynamoDB::Model::BatchGetItemRequest batchGetReq;
 Aws::DynamoDB::Model::KeysAndAttributes keysAndAttrs;
 
 // 取得したいキーのリストを作成
 std::vector<Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue>> keys;
 for (int i = 1; i <= 3; i++) {
    Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue> key;
    key["id"] = toAttr(i);
    keys.push_back(key);
 }
 
 keysAndAttrs.SetKeys(keys);
 batchGetReq.AddRequestItems(tableName, keysAndAttrs);
 
 auto batchGetResult = client.BatchGetItem(batchGetReq);
 if (batchGetResult.IsSuccess()) {
    const auto& responses = batchGetResult.GetResult().GetResponses();
 
    for (const auto& tablePair : responses) {
       qDebug() << "Table:" << QString::fromStdString(tablePair.first);
       qDebug() << "Items count:" << tablePair.second.size();
 
       for (const auto& item : tablePair.second) {
          printItem(item);
       }
    }
 }
 else {
    qDebug() << "✗ Error: " << QString::fromStdString(batchGetResult.GetError().GetMessage());
 }



ページネーション (継続トークン)

大量のデータを取得する際に、ページネーションを使用して分割取得する。

 // ページネーション付きスキャン
 Aws::DynamoDB::Model::ScanRequest scanReq;
 scanReq.SetTableName(tableName);
 scanReq.SetLimit(10);  // 1ページあたり10件
 
 Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue> lastKey;
 int pageNum = 1;
 
 do {
    if (!lastKey.empty()) {
       scanReq.SetExclusiveStartKey(lastKey);
    }
 
    auto scanResult = client.Scan(scanReq);
    if (scanResult.IsSuccess()) {
       const auto& items = scanResult.GetResult().GetItems();
       qDebug() << "\n=== Page" << pageNum << "===" << items.size() << "items";
 
       for (const auto& item : items) {
          printItem(item);
       }
 
       // 次のページがあるか確認
       lastKey = scanResult.GetResult().GetLastEvaluatedKey();
       pageNum++;
    }
    else {
       qDebug() << "✗ Error: " << QString::fromStdString(scanResult.GetError().GetMessage());
        break;
    }
 } while (!lastKey.empty());
 
 qDebug() << "Total pages processed:" << (pageNum - 1);



トランザクション書き込み : TransactWriteItems

複数の操作をトランザクションとして実行する。(全成功または全失敗)

トランザクションは最大100項目まで可能である。

 #include <aws/dynamodb/model/TransactWriteItemsRequest.h>
 #include <aws/dynamodb/model/TransactWriteItem.h>
 
 // トランザクション書き込み
 Aws::DynamoDB::Model::TransactWriteItemsRequest transactReq;
 std::vector<Aws::DynamoDB::Model::TransactWriteItem> transactItems;
 
 // アイテム1: 挿入
 Aws::DynamoDB::Model::TransactWriteItem item1;
 Aws::DynamoDB::Model::Put put1;
 Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue> newItem;
 newItem["id"] = toAttr(100);
 newItem["name"] = toAttr("Transaction User");
 newItem["balance"] = toAttr(1000);
 put1.SetTableName(tableName);
 put1.SetItem(newItem);
 item1.SetPut(put1);
 transactItems.push_back(item1);
 
 // アイテム2: 更新
 Aws::DynamoDB::Model::TransactWriteItem item2;
 Aws::DynamoDB::Model::Update update2;
 update2.SetTableName(tableName);
 Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue> key2;
 key2["id"] = toAttr(101);
 update2.SetKey(key2);
 update2.SetUpdateExpression("SET balance = balance - :amount");
 update2.AddExpressionAttributeValues(":amount", toAttr(100));
 item2.SetUpdate(update2);
 transactItems.push_back(item2);
 
 transactReq.SetTransactItems(transactItems);
 
 auto transactResult = client.TransactWriteItems(transactReq);
 if (transactResult.IsSuccess()) {
    qDebug() << "✓ Transaction completed successfully";
 }
 else {
    qDebug() << "✗ Transaction failed: " << QString::fromStdString(transactResult.GetError().GetMessage());
 }



エラーハンドリング

DynamoDB操作時の一般的なエラーハンドリングパターンを以下に示す。

 auto result = client.GetItem(getReq);
 
 if (!result.IsSuccess()) {
    const auto& error = result.GetError();
 
    switch (error.GetErrorType()) {
       case Aws::DynamoDB::DynamoDBErrors::RESOURCE_NOT_FOUND:
          qDebug() << "テーブルが見つかりません";
          break;
       case Aws::DynamoDB::DynamoDBErrors::PROVISIONED_THROUGHPUT_EXCEEDED:
          qDebug() << "スループット制限を超えました。リトライしてください。";
          break;
       case Aws::DynamoDB::DynamoDBErrors::CONDITIONAL_CHECK_FAILED:
          qDebug() << "条件チェックに失敗しました";
          break;
       case Aws::DynamoDB::DynamoDBErrors::VALIDATION:
          qDebug() << "入力検証エラー: " << QString::fromStdString(error.GetMessage());
          break;
       default:
          qDebug() << "エラー: " << QString::fromStdString(error.GetMessage());
          break;
    }
 }



サンプルコード

以下の例では、AWS DynamoDBにアクセスしてデータを取得、挿入、更新、削除している。

 #include <QCoreApplication>
 #include <QDebug>
 #include <aws/core/Aws.h>
 #include <aws/dynamodb/DynamoDBClient.h>
 #include <aws/dynamodb/model/PutItemRequest.h>
 #include <aws/dynamodb/model/GetItemRequest.h>
 #include <aws/dynamodb/model/DeleteItemRequest.h>
 #include <aws/dynamodb/model/UpdateItemRequest.h>
 #include <aws/dynamodb/model/ScanRequest.h>
 
 // ヘルパー関数
 Aws::DynamoDB::Model::AttributeValue toAttr(const QVariant& value)
 {
    Aws::DynamoDB::Model::AttributeValue attr;
    if (value.typeId() == QMetaType::QString)
       attr.SetS(value.toString().toStdString());
    else if (value.typeId() == QMetaType::Bool)
       attr.SetBool(value.toBool());
    else
       attr.SetN(value.toString().toStdString());
 
    return attr;
 }
 
 void printItem(const Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue> &item)
 {
    qDebug() << "Item:";
    for (const auto& pair : item) {
       QString key = QString::fromStdString(pair.first);
       QString value;
       if (!pair.second.GetS().empty())
          value = QString::fromStdString(pair.second.GetS());
       else if (!pair.second.GetN().empty())
          value = QString::fromStdString(pair.second.GetN());
       else if (pair.second.GetBool())
          value = "true";
 
       qDebug() << "  " << key << ":" << value;
    }
 }
 
 int main(int argc, char *argv[])
 {
    QCoreApplication app(argc, argv);
 
    Aws::SDKOptions options;
    Aws::InitAPI(options);
 
    {
       // DynamoDBクライアント作成 (~/.aws/configからリージョンを自動取得)
       Aws::Client::ClientConfiguration config;
       // リージョンの指定を削除することで、AWS設定ファイルから自動取得
       Aws::DynamoDB::DynamoDBClient client(config);
       const std::string tableName = "<DynamoDBのテーブル名>";
 
       qDebug() << "=== DynamoDB CRUD Demo ===\n";
 
       // 挿入 : PutItem
       qDebug() << "### 1. Insert Item ###";
       Aws::DynamoDB::Model::PutItemRequest putReq;
       putReq.SetTableName(tableName);
       putReq.AddItem("id", toAttr(1));
       putReq.AddItem("name", toAttr("Taro Yamada"));
       putReq.AddItem("age", toAttr(30));
       putReq.AddItem("email", toAttr("taro@example.com"));
 
       if (client.PutItem(putReq).IsSuccess())
           qDebug() << "✓ Item inserted";
 
       // 取得 : GetItem
       qDebug() << "\n### 2. Get Item ###";
       Aws::DynamoDB::Model::GetItemRequest getReq;
       getReq.SetTableName(tableName);
       getReq.AddKey("id", toAttr(1));
 
       auto getResult = client.GetItem(getReq);
       if (getResult.IsSuccess())
           printItem(getResult.GetResult().GetItem());
 
       // 更新 : UpdateItem
       qDebug() << "\n### 3. Update Item ###";
       Aws::DynamoDB::Model::UpdateItemRequest updateReq;
       updateReq.SetTableName(tableName);
       updateReq.AddKey("id", toAttr(1));
       updateReq.SetUpdateExpression("SET age = :val, email = :email");
       updateReq.AddExpressionAttributeValues(":val", toAttr(31));
       updateReq.AddExpressionAttributeValues(":email", toAttr("taro.new@example.com"));
 
       if (client.UpdateItem(updateReq).IsSuccess()) {
           qDebug() << "✓ Item updated";
           printItem(client.GetItem(getReq).GetResult().GetItem());
       }
 
       // 全件取得 : Scan
       qDebug() << "\n### 4. Scan Table ###";
       Aws::DynamoDB::Model::ScanRequest scanReq;
       scanReq.SetTableName(tableName);
 
       auto scanResult = client.Scan(scanReq);
       if (scanResult.IsSuccess()) {
           qDebug() << "Found" << scanResult.GetResult().GetItems().size() << "items";
           for (const auto& item : scanResult.GetResult().GetItems())
              printItem(item);
       }
 
       // 削除 : DeleteItem
       qDebug() << "\n### 5. Delete Item ###";
       Aws::DynamoDB::Model::DeleteItemRequest delReq;
       delReq.SetTableName(tableName);
       delReq.AddKey("id", toAttr(1));
 
       if (client.DeleteItem(delReq).IsSuccess())
          qDebug() << "✓ Item deleted";
    }
 
    Aws::ShutdownAPI(options);
 
    return 0;
 }