翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
Java 用 Amazon QLDB ドライバー — クックブックリファレンス
重要
サポート終了通知: 既存のお客様は、07/31/2025 のサポート終了まで Amazon QLDB を使用できます。詳細については、「Amazon QLDB 台帳を Amazon Aurora PostgreSQL に移行する
このリファレンスガイドでは、Java 用 Amazon QLDB ドライバーの一般的なユースケースについて説明します。Java コード例を提供し、ドライバーを使用して作成、読み取り、更新、および削除 (CRUD) の基本的なオペレーションを実行する方法を説明しています。Amazon Ion データを処理するためのコード例も含まれています。さらに、このガイドでは、トランザクションをべき等にし、一意性の制約を実装するためのベストプラクティスを取り上げています。
注記
該当する場合、一部のユースケースでは、サポートされているメジャーバージョンの Java 用 QLDB ドライバーに対して異なるコード例があります。
目次
ドライバーのインポート
次のコード例では、ドライバー、QLDB セッションクライアント、Amazon Ion パッケージ、およびその他の関連する依存関係をインポートします。
ドライバーのインスタンス化
次のコード例では、指定された台帳名に接続するドライバーインスタンスを作成し、指定された再試行ロジックをカスタム再試行制限で使用します。
注記
この例では、Amazon Ion システムオブジェクト (IonSystem
) もインスタンス化します。このリファレンスでいくつかのデータオペレーションを実行するときに Ion データを処理するには、このオブジェクトが必要です。詳細については、「Amazon Ion の操作」を参照してください。
CRUD オペレーション
QLDB は作成、読み取り、更新、および削除 (CRUD) オペレーションをトランザクションの一部として実行します。
警告
ベストプラクティスとして、書き込みトランザクションを厳密にべき等にしてください。
トランザクションをべき等にする
再試行の場合に予期しない結果を避けるために、書き込みトランザクションをべき等にすることをお勧めします。トランザクションは、複数回実行して毎回同じ結果を生成できる場合、idempotent です。
例えば、Person
という名前のテーブルにドキュメントを挿入するトランザクションについて考えてみます。トランザクションは、まずドキュメントがテーブル内に既に存在するかどうかをチェックする必要があります。このチェックを行わないと、テーブルに重複するドキュメントができる可能性があります。
QLDB がサーバー側でトランザクションを正常にコミットできるが、レスポンスを待機中にクライアントはタイムアウトするとします。トランザクションがべき等でない場合、再試行時に同じドキュメントが複数回挿入される可能性があります。
インデックスを使用してテーブル全体のスキャンを回避する
インデックス付きフィールドまたはドキュメント ID で等価演算子を使用する WHERE
述語句でステートメントを実行する (例: WHERE indexedField = 123
または WHERE indexedField IN (456, 789)
) こともお勧めします。このインデックス付きのルックアップがない場合、QLDB はテーブルスキャンを実行する必要があり、トランザクションタイムアウトやオプティミスティック同時実行制御 (OCC) 競合が発生する可能性があります。
OCC の詳細については、「Amazon QLDB 同時実行モデル」を参照してください。
暗黙的に作成されたトランザクション
QldbDriver.executeExecutor
インスタンスは、暗黙的に作成されたトランザクションをラップします。
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.executeIonValue
引数 (varargs)、または単一の List<IonValue>
引数を受け入れます。ion-javaIonList
が List<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 競合など) が発生した場合にトランザクションを再試行する組み込みの再試行メカニズムがあります。
一意性制約の実装
QLDB はユニークインデックスをサポートしていませんが、この動作をアプリケーションに実装できます。
Person
テーブル内の GovId
フィールドに対して一意性制約を実装するとします。これを行うには、以下を実行するトランザクションを記述します。
-
テーブルに指定された
GovId
を持つ既存のドキュメントがないことをアサートします。 -
アサーションに合格した場合は、ドキュメントを挿入します。
競合するトランザクションが同時にアサーションに合格すると、一方のトランザクションだけが正常にコミットされます。もう一方のトランザクションは 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 の操作
QLDB で Amazon Ion データを処理するには、複数の方法があります。Ion ライブラリ
次のセクションでは、両方の手法を使用して Ion データを処理するコード例について説明します。
Ion パッケージのインポート
アーティファクト ion-java
次の Ion パッケージをインポートします。
import com.amazon.ion.IonStruct; import com.amazon.ion.IonSystem; import com.amazon.ion.system.IonSystemBuilder;
アーティファクト jackson-dataformat-ion2.10.0
以降が必要です。
次の 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 の操作方法の詳細については、GitHub で Amazon Ion のドキュメント