「Qtの応用 - AWS DynamoDB」の版間の差分

提供: MochiuWiki : SUSE, EC, PCB

ページの作成:「== 概要 == AWS DynamoDBは、AWSが提供するフルマネージド型のNoSQLデータベースサービスである。<br> キー・バリュー型およびドキュメント型のデータモデルをサポートし、高速で予測可能なパフォーマンスとシームレスなスケーラビリティを実現する。<br> <br> 主な特徴を以下に示す。<br> * フルマネージド *: サーバ管理、ソフトウェアパッチ、セットアッ…」
 
 
24行目: 24行目:
* プライマリキー
* プライマリキー
*: パーティションキー (必須) と ソートキー (オプション) で構成
*: パーティションキー (必須) と ソートキー (オプション) で構成
<br><br>
<br>
<br>
AWS DynamoDBは永続的な無料利用枠を提供しており、個人利用や小規模なアプリケーションであれば無料枠内で運用できることが多い。<br>
AWS DynamoDBは永続的な無料利用枠を提供しており、個人利用や小規模なアプリケーションであれば無料枠内で運用できることが多い。<br>

2025年11月18日 (火) 20:26時点における最新版

概要

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

主な特徴を以下に示す。

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


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

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


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


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

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


1件のデータ挿入 : PutItem

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

<syntaxhighlight lang="c++">
// アイテムを挿入
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());
}
</syntaxhighlight>



1件のデータ取得 :GetItem

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

<syntaxhighlight lang="c++">
// プライマリキーでアイテムを取得
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());
}
</syntaxhighlight>



複数件のデータ取得 : Query

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

<syntaxhighlight lang="c++">
// クエリで複数件取得 (パーティションキーでフィルタ)
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());
}
</syntaxhighlight>



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

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

<syntaxhighlight lang="c++">
// パーティションキーとソートキーでクエリ
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);
   }
}
</syntaxhighlight>



全件取得 : Scan

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

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

<syntaxhighlight lang="c++">
// テーブル全体をスキャン
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());
}
</syntaxhighlight>



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

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

<syntaxhighlight lang="c++">

// 条件付きスキャン(例: 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);
   }
}
</syntaxhighlight>



データの更新 : UpdateItem

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

<syntaxhighlight lang="c++">
// アイテムを更新
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());
}
</syntaxhighlight>



条件付き更新

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

<syntaxhighlight lang="c++">
// 条件付き更新(例: 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());
   }
}
</syntaxhighlight>



データの削除 : DeleteItem

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

<syntaxhighlight lang="c++">
// アイテムを削除
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());
}
</syntaxhighlight>



条件付き削除

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

<syntaxhighlight lang="c++">
// 条件付き削除
// 例: 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";
   }
}
</syntaxhighlight>



バッチ書き込み : BatchWriteItem

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

<syntaxhighlight lang="c++">
#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());
}
</syntaxhighlight>



バッチ読み込み : BatchGetItem

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

<syntaxhighlight lang="c++">
#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());
}
</syntaxhighlight>



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

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

<syntaxhighlight lang="c++">
// ページネーション付きスキャン
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);
</syntaxhighlight>



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

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

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

<syntaxhighlight lang="c++">
#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());
}

</syntaxhighlight>

エラーハンドリング

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

<syntaxhighlight lang="c++">
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;
   }
}
</syntaxhighlight>



サンプルコード

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

<syntaxhighlight lang="c++">
#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;
}
</syntaxhighlight>