翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
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 パッケージ、およびその他の関連する依存関係をインポートします。
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;
ドライバーのインスタンス化
次のコード例では、指定された台帳名に接続するドライバーインスタンスを作成し、指定された再試行ロジックをカスタム再試行制限で使用します。
注記
この例では、Amazon Ion システムオブジェクト (IonSystem
) もインスタンス化します。このリファレンスでいくつかのデータオペレーションを実行するときに Ion データを処理するには、このオブジェクトが必要です。詳細については、「Amazon Ion の操作」を参照してください。
QldbDriver qldbDriver = QldbDriver.builder()
.ledger("vehicle-registration")
.transactionRetryPolicy(RetryPolicy
.builder()
.maxRetries(3)
.build())
.sessionClientBuilder(QldbSessionClient.builder())
.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.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競合など) が発生した場合にトランザクションを再試行する再試行メカニズムが組み込まれています。
再試行の最大数とバックオフ戦略を設定できます。
デフォルトの再試行制限は で4
、デフォルトのバックオフ戦略は ですDefaultQldbTransactionBackoffStrategy
次のコード例では、ドライバーのインスタンスのカスタム再試行制限とカスタムバックオフ戦略を使用して再試行ロジックを指定します。
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());
}
}
一意性制約の実装
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 の操作
で Amazon Ion データを処理するには、複数の方法がありますQLDB。Ion ライブラリ
次のセクションでは、両方の手法を使用して Ion データを処理するコード例について説明します。
Ion パッケージのインポート
アーティファクト ion-java
dependencies { compile group: 'com.amazon.ion', name: 'ion-java', version: '1.6.1' }
次の 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 の操作の詳細については、 の Amazon Ion ドキュメント