Java 用 Amazon QLDBドライバー — クックブックリファレンス - Amazon Quantum 台帳データベース (Amazon QLDB)

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

Java 用 Amazon QLDBドライバー — クックブックリファレンス

重要

サポート終了通知: 既存のお客様は、07/31/2025 のサポート終了QLDBまで Amazon を使用できます。詳細については、「Amazon Ledger QLDB を Amazon Aurora Postgre に移行するSQL」を参照してください。

このリファレンスガイドでは、Java 用 Amazon QLDBドライバーの一般的なユースケースについて説明します。ドライバーを使用して基本的な作成、読み取り、更新、削除 (CRUD) オペレーションを実行する方法を示す Java コード例を示します。Amazon Ion データを処理するためのコード例も含まれています。さらに、このガイドでは、トランザクションをべき等にし、一意性の制約を実装するためのベストプラクティスを取り上げています。

注記

該当する場合、一部のユースケースでは、サポートされている Java 用QLDBドライバーのメジャーバージョンごとに異なるコード例があります。

ドライバーのインポート

次のコード例では、ドライバー、QLDBセッションクライアント、Amazon Ion パッケージ、およびその他の関連する依存関係をインポートします。

2.x
import com.amazon.ion.IonStruct; import com.amazon.ion.IonSystem; import com.amazon.ion.IonValue; import com.amazon.ion.system.IonSystemBuilder; import software.amazon.awssdk.services.qldbsession.QldbSessionClient; import software.amazon.qldb.QldbDriver; import software.amazon.qldb.Result;
1.x
import com.amazon.ion.IonStruct; import com.amazon.ion.IonSystem; import com.amazon.ion.IonValue; import com.amazon.ion.system.IonSystemBuilder; import com.amazonaws.services.qldbsession.AmazonQLDBSessionClientBuilder; import software.amazon.qldb.PooledQldbDriver; import software.amazon.qldb.Result;

ドライバーのインスタンス化

次のコード例では、指定された台帳名に接続するドライバーインスタンスを作成し、指定された再試行ロジックをカスタム再試行制限で使用します。

注記

この例では、Amazon Ion システムオブジェクト (IonSystem) もインスタンス化します。このリファレンスでいくつかのデータオペレーションを実行するときに Ion データを処理するには、このオブジェクトが必要です。詳細については、「Amazon Ion の操作」を参照してください。

2.x
QldbDriver qldbDriver = QldbDriver.builder() .ledger("vehicle-registration") .transactionRetryPolicy(RetryPolicy .builder() .maxRetries(3) .build()) .sessionClientBuilder(QldbSessionClient.builder()) .build(); IonSystem SYSTEM = IonSystemBuilder.standard().build();
1.x
PooledQldbDriver qldbDriver = PooledQldbDriver.builder() .withLedger("vehicle-registration") .withRetryLimit(3) .withSessionClientBuilder(AmazonQLDBSessionClientBuilder.standard()) .build(); IonSystem SYSTEM = IonSystemBuilder.standard().build();

CRUD オペレーション

QLDB は、トランザクションの一部として作成、読み取り、更新、削除 (CRUD) オペレーションを実行します。

警告

ベストプラクティスとして、書き込みトランザクションを厳密にべき等にしてください。

トランザクションをべき等にする

再試行の場合に予期しない結果を避けるために、書き込みトランザクションをべき等にすることをお勧めします。トランザクションは、複数回実行して毎回同じ結果を生成できる場合、idempotent です。

例えば、Person という名前のテーブルにドキュメントを挿入するトランザクションについて考えてみます。トランザクションは、まずドキュメントがテーブル内に既に存在するかどうかをチェックする必要があります。このチェックを行わないと、テーブルに重複するドキュメントができる可能性があります。

がサーバー側でトランザクションをQLDB正常にコミットしたが、レスポンスを待っている間にクライアントがタイムアウトするとします。トランザクションがべき等でない場合、再試行時に同じドキュメントが複数回挿入される可能性があります。

インデックスを使用してテーブル全体のスキャンを回避する

インデックス付きフィールドまたはドキュメント ID で等価演算子を使用する WHERE 述語句でステートメントを実行する (例: WHERE indexedField = 123 または WHERE indexedField IN (456, 789)) こともお勧めします。このインデックス付きルックアップがないと、 はテーブルスキャンを行うQLDB必要があり、トランザクションのタイムアウトやオプティミスティック同時実行制御 () の競合が発生する可能性があります。OCC

OCC の詳細については、「Amazon QLDB 同時実行モデル」を参照してください。

暗黙的に作成されたトランザクション

QldbDriver.execute メソッドは、Executor のインスタンスを受信する Lambda 関数を受け入れます。この関数は、ステートメントの実行に使用できます。Executor インスタンスは、暗黙的に作成されたトランザクションをラップします。

Executor.execute メソッドを使用して、Lambda 関数内でステートメントを実行できます。ドライバーは、Lambda 関数が戻ったときに暗黙的にトランザクションをコミットします。

以下のセクションでは、基本的なCRUDオペレーションの実行、カスタム再試行ロジックの指定、一意性制約の実装方法について説明します。

注記

該当する場合、これらのセクションでは、組み込みの Ion ライブラリと Jackson Ion マッパーライブラリの両方を使用して Amazon Ion データを処理するコード例を示します。詳細については、「Amazon Ion の操作」を参照してください。

テーブルの作成

qldbDriver.execute(txn -> { txn.execute("CREATE TABLE Person"); });

インデックスの作成

qldbDriver.execute(txn -> { txn.execute("CREATE INDEX ON Person(GovId)"); });

ドキュメントの読み取り

// Assumes that Person table has documents as follows: // { GovId: "TOYENC486FH", FirstName: "Brent" } qldbDriver.execute(txn -> { Result result = txn.execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'"); IonStruct person = (IonStruct) result.iterator().next(); System.out.println(person.get("GovId")); // prints TOYENC486FH System.out.println(person.get("FirstName")); // prints Brent });

クエリパラメータの使用

次のコード例では、Ion 型のクエリパラメータを使用します。

qldbDriver.execute(txn -> { Result result = txn.execute("SELECT * FROM Person WHERE GovId = ?", SYSTEM.newString("TOYENC486FH")); IonStruct person = (IonStruct) result.iterator().next(); System.out.println(person.get("GovId")); // prints TOYENC486FH System.out.println(person.get("FirstName")); // prints Brent });

次のコード例では、複数のクエリパラメータを使用します。

qldbDriver.execute(txn -> { Result result = txn.execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", SYSTEM.newString("TOYENC486FH"), SYSTEM.newString("Brent")); IonStruct person = (IonStruct) result.iterator().next(); System.out.println(person.get("GovId")); // prints TOYENC486FH System.out.println(person.get("FirstName")); // prints Brent });

次のコード例では、クエリパラメータのリストを使用します。

qldbDriver.execute(txn -> { final List<IonValue> parameters = new ArrayList<>(); parameters.add(SYSTEM.newString("TOYENC486FH")); parameters.add(SYSTEM.newString("ROEE1")); parameters.add(SYSTEM.newString("YH844")); Result result = txn.execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", parameters); IonStruct person = (IonStruct) result.iterator().next(); System.out.println(person.get("GovId")); // prints TOYENC486FH System.out.println(person.get("FirstName")); // prints Brent });
// Assumes that Person table has documents as follows: // {GovId: "TOYENC486FH", FirstName: "Brent" } qldbDriver.execute(txn -> { try { Result result = txn.execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'"); Person person = MAPPER.readValue(result.iterator().next(), Person.class); System.out.println(person.getFirstName()); // prints Brent System.out.println(person.getGovId()); // prints TOYENC486FH } catch (IOException e) { e.printStackTrace(); } });

クエリパラメータの使用

次のコード例では、Ion 型のクエリパラメータを使用します。

qldbDriver.execute(txn -> { try { Result result = txn.execute("SELECT * FROM Person WHERE GovId = ?", MAPPER.writeValueAsIonValue("TOYENC486FH")); Person person = MAPPER.readValue(result.iterator().next(), Person.class); System.out.println(person.getFirstName()); // prints Brent System.out.println(person.getGovId()); // prints TOYENC486FH } catch (IOException e) { e.printStackTrace(); } });

次のコード例では、複数のクエリパラメータを使用します。

qldbDriver.execute(txn -> { try { Result result = txn.execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", MAPPER.writeValueAsIonValue("TOYENC486FH"), MAPPER.writeValueAsIonValue("Brent")); Person person = MAPPER.readValue(result.iterator().next(), Person.class); System.out.println(person.getFirstName()); // prints Brent System.out.println(person.getGovId()); // prints TOYENC486FH } catch (IOException e) { e.printStackTrace(); } });

次のコード例では、クエリパラメータのリストを使用します。

qldbDriver.execute(txn -> { try { final List<IonValue> parameters = new ArrayList<>(); parameters.add(MAPPER.writeValueAsIonValue("TOYENC486FH")); parameters.add(MAPPER.writeValueAsIonValue("ROEE1")); parameters.add(MAPPER.writeValueAsIonValue("YH844")); Result result = txn.execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", parameters); Person person = MAPPER.readValue(result.iterator().next(), Person.class); System.out.println(person.getFirstName()); // prints Brent System.out.println(person.getGovId()); // prints TOYENC486FH } catch (IOException e) { e.printStackTrace(); } });
注記

インデックス付きルックアップなしでクエリを実行すると、完全なテーブルスキャンが呼び出されます。この例では、GovId フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId、クエリのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。

ドキュメントの挿入

次のコード例では、Ion データ型を挿入します。

qldbDriver.execute(txn -> { // Check if a document with GovId:TOYENC486FH exists // This is critical to make this transaction idempotent Result result = txn.execute("SELECT * FROM Person WHERE GovId = ?", SYSTEM.newString("TOYENC486FH")); // Check if there is a result if (!result.iterator().hasNext()) { IonStruct person = SYSTEM.newEmptyStruct(); person.put("GovId").newString("TOYENC486FH"); person.put("FirstName").newString("Brent"); // Insert the document txn.execute("INSERT INTO Person ?", person); } });

次のコード例では、Ion データ型を挿入します。

qldbDriver.execute(txn -> { try { // Check if a document with GovId:TOYENC486FH exists // This is critical to make this transaction idempotent Result result = txn.execute("SELECT * FROM Person WHERE GovId = ?", MAPPER.writeValueAsIonValue("TOYENC486FH")); // Check if there is a result if (!result.iterator().hasNext()) { // Insert the document txn.execute("INSERT INTO Person ?", MAPPER.writeValueAsIonValue(new Person("Brent", "TOYENC486FH"))); } } catch (IOException e) { e.printStackTrace(); } });

このトランザクションは、ドキュメントを Person テーブルに挿入します。挿入する前に、まずドキュメントがテーブル内に既に存在しているかどうかをチェックします。このチェックにより、トランザクションは本質的にべき等になります。このトランザクションを複数回実行しても、意図しない副作用は発生しません。

注記

この例では、GovId フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId、ステートメントのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。

1 つのステートメントで複数のドキュメントの挿入

1 つのINSERTステートメントを使用して複数のドキュメントを挿入するには、次のように タイプのパラメータ IonList ( として明示的にキャストIonValue) をステートメントに渡すことができます。

// people is an IonList explicitly cast as an IonValue txn.execute("INSERT INTO People ?", (IonValue) people);

IonList を渡すときは、変数プレースホルダー (?) を二重山括弧 (<<...>>) で囲まないでください。マニュアルの PartiQL ステートメントでは、二重山括弧はバッグと呼ばれる順序付けされていないコレクションを表します。

TransactionExecutor.execute メソッドはオーバーロードされます。これは、可変数の IonValue 引数 (varargs)、または単一の List<IonValue> 引数を受け入れます。ion-java では、IonListList<IonValue> として実装されます。

Java では、オーバーロードされたメソッドを呼び出すと、デフォルトで最も具体的なメソッド実装が使用されます。この場合、IonList パラメータを渡すと、List<IonValue> を使用するメソッドがデフォルトで使用されます。このメソッド実装は、呼び出されるとリストの IonValue 要素を個別の値として渡します。したがって、代わりに varargs メソッドを呼び出すには、IonList パラメータを IonValue に明示的にキャストする必要があります。

ドキュメントの更新

qldbDriver.execute(txn -> { final List<IonValue> parameters = new ArrayList<>(); parameters.add(SYSTEM.newString("John")); parameters.add(SYSTEM.newString("TOYENC486FH")); txn.execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", parameters); });
qldbDriver.execute(txn -> { try { final List<IonValue> parameters = new ArrayList<>(); parameters.add(MAPPER.writeValueAsIonValue("John")); parameters.add(MAPPER.writeValueAsIonValue("TOYENC486FH")); txn.execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", parameters); } catch (IOException e) { e.printStackTrace(); } });
注記

この例では、GovId フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId、ステートメントのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。

ドキュメントの削除

qldbDriver.execute(txn -> { txn.execute("DELETE FROM Person WHERE GovId = ?", SYSTEM.newString("TOYENC486FH")); });
qldbDriver.execute(txn -> { try { txn.execute("DELETE FROM Person WHERE GovId = ?", MAPPER.writeValueAsIonValue("TOYENC486FH")); } catch (IOException e) { e.printStackTrace(); } });
注記

この例では、GovId フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId、ステートメントのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。

トランザクションで複数のステートメントの実行

// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd // set your UPDATE to filter on vin and insured, and check if you updated something or not. public static boolean InsureCar(QldbDriver qldbDriver, final String vin) { final IonSystem ionSystem = IonSystemBuilder.standard().build(); final IonString ionVin = ionSystem.newString(vin); return qldbDriver.execute(txn -> { Result result = txn.execute( "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", ionVin); if (!result.isEmpty()) { txn.execute("UPDATE Vehicles SET insured = TRUE WHERE vin = ?", ionVin); return true; } return false; }); }

再試行ロジック

ドライバーの executeメソッドには、再試行可能な例外 (タイムアウトやOCC競合など) が発生した場合にトランザクションを再試行する再試行メカニズムが組み込まれています。

2.x

再試行の最大数とバックオフ戦略を設定できます。

デフォルトの再試行制限は で4、デフォルトのバックオフ戦略は ですDefaultQldbTransactionBackoffStrategy。のインスタンスを使用して、ドライバーインスタンスごと、およびトランザクションごとに再試行設定を設定できますRetryPolicy

次のコード例では、ドライバーのインスタンスのカスタム再試行制限とカスタムバックオフ戦略を使用して再試行ロジックを指定します。

public void retry() { QldbDriver qldbDriver = QldbDriver.builder() .ledger("vehicle-registration") .transactionRetryPolicy(RetryPolicy.builder() .maxRetries(2) .backoffStrategy(new CustomBackOffStrategy()).build()) .sessionClientBuilder(QldbSessionClient.builder()) .build(); } private class CustomBackOffStrategy implements BackoffStrategy { @Override public Duration calculateDelay(RetryPolicyContext retryPolicyContext) { return Duration.ofMillis(1000 * retryPolicyContext.retriesAttempted()); } }

次のコード例では、特定のトランザクションのカスタム再試行制限とカスタムバックオフ戦略を使用して再試行ロジックを指定します。execute のこの設定は、ドライバーインスタンスに設定された再試行ロジックを上書きします。

public void retry() { Result result = qldbDriver.execute(txn -> { txn.execute("SELECT * FROM Person WHERE GovId = ?", SYSTEM.newString("TOYENC486FH")); }, RetryPolicy.builder() .maxRetries(2) .backoffStrategy(new CustomBackOffStrategy()) .build()); } private class CustomBackOffStrategy implements BackoffStrategy { // Configuring a custom backoff which increases delay by 1s for each attempt. @Override public Duration calculateDelay(RetryPolicyContext retryPolicyContext) { return Duration.ofMillis(1000 * retryPolicyContext.retriesAttempted()); } }
1.x

再試行の最大数は設定可能です。再試行制限を設定するには、PooledQldbDriver の初期化時に retryLimit プロパティを設定します。

デフォルトの再試行制限回数は 4 です。

一意性制約の実装

QLDB は一意のインデックスをサポートしていませんが、この動作をアプリケーションに実装できます。

Person テーブル内の GovId フィールドに対して一意性制約を実装するとします。これを行うには、以下を実行するトランザクションを記述します。

  1. テーブルに指定された GovId を持つ既存のドキュメントがないことをアサートします。

  2. アサーションに合格した場合は、ドキュメントを挿入します。

競合するトランザクションが同時にアサーションに合格すると、一方のトランザクションだけが正常にコミットされます。他のトランザクションはOCC競合例外で失敗します。

次のコード例は、この一意性制約ロジックを実装する方法を示しています。

qldbDriver.execute(txn -> { Result result = txn.execute("SELECT * FROM Person WHERE GovId = ?", SYSTEM.newString("TOYENC486FH")); // Check if there is a result if (!result.iterator().hasNext()) { IonStruct person = SYSTEM.newEmptyStruct(); person.put("GovId").newString("TOYENC486FH"); person.put("FirstName").newString("Brent"); // Insert the document txn.execute("INSERT INTO Person ?", person); } });
注記

この例では、GovId フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId、ステートメントのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。

Amazon Ion の操作

で Amazon Ion データを処理するには、複数の方法がありますQLDB。Ion ライブラリから組み込みメソッドを使用するには、必要に応じて、ドキュメントを柔軟に作成および変更します。または、Ion 用の高速 XMLの Jackson データ形式モジュールを使用して、Ion ドキュメントをプレーンな古い Java オブジェクト (POJO) モデルにマッピングすることもできます。 https://github.com/FasterXML/jackson-dataformats-binary/tree/master/ion

次のセクションでは、両方の手法を使用して Ion データを処理するコード例について説明します。

Ion パッケージのインポート

アーティファクト ion-java を Java プロジェクトの依存関係として追加します。

Gradle
dependencies { compile group: 'com.amazon.ion', name: 'ion-java', version: '1.6.1' }
Maven
<dependencies> <dependency> <groupId>com.amazon.ion</groupId> <artifactId>ion-java</artifactId> <version>1.6.1</version> </dependency> </dependencies>

次の Ion パッケージをインポートします。

import com.amazon.ion.IonStruct; import com.amazon.ion.IonSystem; import com.amazon.ion.system.IonSystemBuilder;

アーティファクトを依存関係jackson-dataformat-ionとして Java プロジェクトに追加します。QLDB にはバージョン 2.10.0 以降が必要です。

Gradle
dependencies { compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-ion', version: '2.10.0' }
Maven
<dependencies> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-ion</artifactId> <version>2.10.0</version> </dependency> </dependencies>

次の Ion パッケージをインポートします。

import com.amazon.ion.IonReader; import com.amazon.ion.IonStruct; import com.amazon.ion.system.IonReaderBuilder; import com.amazon.ion.system.IonSystemBuilder; import com.fasterxml.jackson.dataformat.ion.IonObjectMapper; import com.fasterxml.jackson.dataformat.ion.ionvalue.IonValueMapper;

Ion の初期化

IonSystem SYSTEM = IonSystemBuilder.standard().build();
IonObjectMapper MAPPER = new IonValueMapper(IonSystemBuilder.standard().build());

Ion オブジェクトの作成

次のコード例では、IonStruct インターフェイスとその組み込みメソッドを使用して Ion オブジェクトを作成します。

IonStruct ionStruct = SYSTEM.newEmptyStruct(); ionStruct.put("GovId").newString("TOYENC486FH"); ionStruct.put("FirstName").newString("Brent"); System.out.println(ionStruct.toPrettyString()); // prints a nicely formatted copy of ionStruct

Person次のように、 という名前の JSONマップモデルクラスがあるとします。

import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; public static class Person { private final String firstName; private final String govId; @JsonCreator public Person(@JsonProperty("FirstName") final String firstName, @JsonProperty("GovId") final String govId) { this.firstName = firstName; this.govId = govId; } @JsonProperty("FirstName") public String getFirstName() { return firstName; } @JsonProperty("GovId") public String getGovId() { return govId; } }

次のコード例では、Person のインスタンスから IonStruct オブジェクトを作成します。

IonStruct ionStruct = (IonStruct) MAPPER.writeValueAsIonValue(new Person("Brent", "TOYENC486FH"));

Ion オブジェクトの作成

次のコード例では、ionStruct インスタンスの各フィールドを出力します。

// ionStruct is an instance of IonStruct System.out.println(ionStruct.get("GovId")); // prints TOYENC486FH System.out.println(ionStruct.get("FirstName")); // prints Brent

次のコード例では、IonStruct オブジェクトを読み取り、Person のインスタンスにマップします。

// ionStruct is an instance of IonStruct IonReader reader = IonReaderBuilder.standard().build(ionStruct); Person person = MAPPER.readValue(reader, Person.class); System.out.println(person.getFirstName()); // prints Brent System.out.println(person.getGovId()); // prints TOYENC486FH

Ion の操作の詳細については、 の Amazon Ion ドキュメントを参照してください GitHub。で Ion を使用するその他のコード例についてはQLDB、「」を参照してくださいAmazon での Amazon Ion データ型の使用 QLDB