本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
Java 的 Amazon QLDB 驅動程序-食譜參考
重要
支援結束通知:現有客戶將能夠使用 Amazon,QLDB直到 2025 年 7 月 31 日終止支援為止。有關更多詳細信息,請參閱將 Amazon QLDB 分類帳遷移到 Amazon Aurora 郵政. SQL
本參考指南顯示適用於 Java 的 Amazon QLDB 驅動程式的常見使用案例。它提供了 Java 代碼示例,演示瞭如何使用驅動程序來運行基本的創建,讀取,更新和刪除(CRUD)操作。它還包括用於處理 Amazon Ion 資料的程式碼範例。此外,本指南還重點介紹了使交易冪等和實施唯一性約束的最佳實踐。
注意
在適用的情況下,某些使用案例會針對每個支援的 Java QLDB 驅動程式主要版本提供不同的程式碼範例。
內容
匯入驅動程式
下列程式碼範例會匯入驅動程式、QLDB工作階段用戶端、Amazon Ion 套件及其他相關相依性。
實例化驅動程序
下列程式碼範例會建立連線至指定分類帳名稱的驅動程式執行環境,並使用具有自訂重試限制的指定重試邏輯。
注意
此範例也會實體化 Amazon 離子系統物件 (IonSystem
)。在此參考中執行某些資料作業時,您需要此物件來處理 Ion 資料。如需進一步了解,請參閱與 Amazon 離子工作。
CRUD操作
QLDB在交易中執行建立、讀取、更新和刪除 (CRUD) 作業。
警告
作為最佳實踐,使您的寫入事務嚴格為冪等。
使交易冪等
我們建議您將寫入事務設為冪等,以避免在重試的情況下出現任何意外的副作用。如果事務可以運行多次並每次產生相同的結果,則事務是冪等的。
例如,假設將文件插入名為的資料表中的交易Person
。事務應該首先檢查文檔是否已經存在於表中。如果沒有此檢查,表格最終可能會出現重複的文件。
假設成QLDB功地在服務器端提交事務,但客戶端在等待響應時超時。如果事務不是冪等的,則在重試的情況下,可以多次插入相同的文檔。
使用索引來避免完整表格掃描
我們也建議您在索引欄位或文件 ID 上使用相等運算子來執行具有述WHERE
詞子句的陳述式;例如,WHERE indexedField = 123
或WHERE indexedField IN (456, 789)
。如果沒有此索引查找,則QLDB需要進行表掃描,這可能會導致事務超時或樂觀的並發控制(OCC)衝突。
如需有關 OCC 的詳細資訊,請參閱 Amazon QLDB 并发模型。
隱含建立的交易
QldbDriver.executeExecutor
體會封裝隱含建立的交易。
您可以使用方法在 lambda 函數中執行陳述Executor.execute
式。當 lambda 函數返回時,驅動程序隱式地提交交易。
下列各節說明如何執行基本CRUD作業、指定自訂重試邏輯,以及實作唯一性條件約束。
注意
在適用的情況下,這些區段提供使用內建 Ion 程式庫和 Jackson Ion 映射程式庫來處理 Amazon Ion 資料的程式碼範例。如需進一步了解,請參閱與 Amazon 離子工作。
建立資料表
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衝突例外狀況或交易逾時。
在一個語句中插入多個文檔
若要使用單一INSERT陳述式插入多個文件,您可以將類型的參數 IonList(明確轉換為 aIonValue
) 傳遞至陳述式,如下所示。
// people is an IonList explicitly cast as an IonValue txn.execute("INSERT INTO People ?", (IonValue) people);
傳遞的時候,您不會將變數預留位置 (?
) 括在雙角括號 (<<...>>
) 中IonList
。在手動 PartiQL 陳述式中,雙角括號表示稱為袋子的無序集合。
該 TransactionExecutor.executeIonValue
參數(可變參數)或單List<IonValue>
個參數。在離子 JavaIonList
被實現為一個. List<IonValue>
當您調用重載方法時,Java 默認為最具體的方法實現。在這種情況下,當您傳遞IonList
參數時,它會預設為採用List<IonValue>
. 調用時,此方法實現將列表中的IonValue
元素作為不同的值傳遞。因此,要調用可變參數方法,您必須明確地將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 離子工作
有多種方法可以在中處理 Amazon 離子數據QLDB。您可以使用 Ion 庫
以下各節提供使用這兩種技術處理離子資料的程式碼範例。
匯入離子套件
將工件離子 Java 添加為 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;
初始化離子
IonSystem SYSTEM = IonSystemBuilder.standard().build();
IonObjectMapper MAPPER = new IonValueMapper(IonSystemBuilder.standard().build());
建立離子物件
下列程式碼範例會使用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
假設您有一個名為的 JSON-map 模型類別Person
,如下所示。
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; } }
下列程式碼範例會從的執行個體建立IonStruct
物件Person
。
IonStruct ionStruct = (IonStruct) MAPPER.writeValueAsIonValue(new Person("Brent", "TOYENC486FH"));
讀取離子物件
下列程式碼範例會列印執行個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 文件