QLDBDriver Amazon per Java: riferimento al Cookbook - Database Amazon Quantum Ledger (Amazon) QLDB

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

QLDBDriver Amazon per Java: riferimento al Cookbook

Importante

Avviso di fine del supporto: i clienti esistenti potranno utilizzare Amazon QLDB fino alla fine del supporto il 31/07/2025. Per ulteriori dettagli, consulta Migrare un Amazon QLDB Ledger ad Amazon Aurora Postgre. SQL

Questa guida di riferimento mostra i casi d'uso comuni del QLDB driver Amazon per Java. Fornisce esempi di codice Java che dimostrano come utilizzare il driver per eseguire operazioni di base di creazione, lettura, aggiornamento ed eliminazione (CRUD). Include anche esempi di codice per l'elaborazione dei dati di Amazon Ion. Inoltre, questa guida illustra le migliori pratiche per rendere le transazioni idempotenti e implementare vincoli di unicità.

Nota

Ove applicabile, alcuni casi d'uso hanno esempi di codice diversi per ogni versione principale supportata del driver per Java. QLDB

Importazione del driver

Il seguente esempio di codice importa il driver, il client di QLDB sessione, i pacchetti Amazon Ion e altre dipendenze correlate.

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;

Istanziazione del driver

Il seguente esempio di codice crea un'istanza di driver che si connette a un nome di registro specificato e utilizza una logica di ripetizione specificata con un limite di tentativi personalizzato.

Nota

Questo esempio crea anche un'istanza di un oggetto di sistema Amazon Ion ()IonSystem. È necessario questo oggetto per elaborare i dati Ion durante l'esecuzione di alcune operazioni sui dati in questo riferimento. Per ulteriori informazioni, consulta Lavorare con 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();

CRUDoperazioni

QLDBesegue operazioni di creazione, lettura, aggiornamento e cancellazione (CRUD) come parte di una transazione.

avvertimento

Come buona pratica, rendi le tue transazioni di scrittura strettamente idempotenti.

Rendere le transazioni idempotenti

Si consiglia di rendere le transazioni di scrittura idempotenti per evitare effetti collaterali imprevisti in caso di nuovi tentativi. Una transazione è idempotente se può essere eseguita più volte e produrre risultati identici ogni volta.

Ad esempio, si consideri una transazione che inserisce un documento in una tabella denominata. Person La transazione deve innanzitutto verificare se il documento esiste già o meno nella tabella. Senza questo controllo, la tabella potrebbe finire con documenti duplicati.

Supponiamo che QLDB esegua correttamente il commit della transazione sul lato server, ma che il client scada durante l'attesa di una risposta. Se la transazione non è idempotente, lo stesso documento potrebbe essere inserito più di una volta in caso di nuovo tentativo.

Utilizzo degli indici per evitare scansioni complete della tabella

Si consiglia inoltre di eseguire istruzioni con una clausola di WHERE predicato utilizzando un operatore di uguaglianza su un campo indicizzato o un ID di documento, ad esempio o. WHERE indexedField = 123 WHERE indexedField IN (456, 789) Senza questa ricerca indicizzata, QLDB deve eseguire una scansione della tabella, che può portare a timeout delle transazioni o a conflitti ottimistici di controllo della concorrenza (). OCC

Per ulteriori informazioni su OCC, consultare Modello di QLDB concorrenza Amazon.

Transazioni create implicitamente

Il metodo QldbDriver.execute accetta una funzione lambda che riceve un'istanza di Executor, che può essere utilizzata per eseguire istruzioni. L'Executoristanza racchiude una transazione creata implicitamente.

È possibile eseguire istruzioni all'interno della funzione lambda utilizzando il metodo. Executor.execute Il driver esegue implicitamente la transazione quando ritorna la funzione lambda.

Le sezioni seguenti mostrano come eseguire CRUD operazioni di base, specificare una logica di ripetizione personalizzata e implementare vincoli di unicità.

Nota

Ove applicabile, queste sezioni forniscono esempi di codice per l'elaborazione dei dati di Amazon Ion utilizzando sia la libreria Ion integrata che la libreria Jackson Ion mapper. Per ulteriori informazioni, consulta Lavorare con Amazon Ion.

Creazione di tabelle

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

Creazione di indici

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

Leggere documenti

// 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 });

Utilizzo dei parametri di interrogazione

Il seguente esempio di codice utilizza un parametro di query di tipo 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 });

Il seguente esempio di codice utilizza più parametri di query.

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 });

Il seguente esempio di codice utilizza un elenco di parametri di query.

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(); } });

Utilizzo dei parametri di interrogazione

Il seguente esempio di codice utilizza un parametro di query di tipo 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(); } });

Il seguente esempio di codice utilizza più parametri di query.

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(); } });

Il seguente esempio di codice utilizza un elenco di parametri di query.

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(); } });
Nota

Quando si esegue una query senza una ricerca indicizzata, viene richiamata una scansione completa della tabella. In questo esempio, si consiglia di disporre di un indice sul campo per ottimizzare le GovId prestazioni. Senza un indice attivoGovId, le query possono avere una maggiore latenza e possono anche portare a eccezioni di OCC conflitto o a timeout delle transazioni.

Inserimento di documenti

Il seguente esempio di codice inserisce i tipi di dati 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); } });

Il seguente esempio di codice inserisce i tipi di dati 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(); } });

Questa transazione inserisce un documento nella Person tabella. Prima dell'inserimento, controlla innanzitutto se il documento esiste già nella tabella. Questo controllo rende la transazione di natura idempotente. Anche se esegui questa transazione più volte, non causerà effetti collaterali indesiderati.

Nota

In questo esempio, consigliamo di avere un indice sul GovId campo per ottimizzare le prestazioni. Senza un indice attivoGovId, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni di OCC conflitto o a timeout delle transazioni.

Inserimento di più documenti in un'unica dichiarazione

Per inserire più documenti utilizzando una sola INSERT istruzione, potete passare un parametro di tipo IonList(espresso esplicitamente come unIonValue) all'istruzione come segue.

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

Non racchiudete la variabile placeholder (?) tra parentesi angolari doppie () quando passate un. <<...>> IonList Nelle istruzioni PartiQL manuali, le parentesi doppie angolari indicano una raccolta non ordinata nota come borsa.

Il metodo TransactionExecutor.execute è sovraccarico. Accetta un numero variabile di IonValue argomenti (varargs) o un singolo argomento. List<IonValue> In ion-java, IonList è implementato come. List<IonValue>

Java utilizza per impostazione predefinita l'implementazione del metodo più specifica quando si chiama un metodo sovraccaricato. In questo caso, quando si passa un IonList parametro, per impostazione predefinita viene utilizzato il metodo che richiede un. List<IonValue> Quando viene richiamata, questa implementazione del metodo passa IonValue gli elementi dell'elenco come valori distinti. Quindi, per richiamare invece il metodo varargs, è necessario eseguire il cast esplicito di un parametro come. IonList IonValue

Aggiornamento dei documenti

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(); } });
Nota

In questo esempio, consigliamo di avere un indice sul GovId campo per ottimizzare le prestazioni. Senza un indice attivoGovId, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni di OCC conflitto o a timeout delle transazioni.

Eliminazione di documenti

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(); } });
Nota

In questo esempio, consigliamo di avere un indice sul GovId campo per ottimizzare le prestazioni. Senza un indice attivoGovId, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni di OCC conflitto o a timeout delle transazioni.

Esecuzione di più istruzioni in una transazione

// 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; }); }

Logica di ripetizione dei tentativi

Il execute metodo del driver dispone di un meccanismo di riprova integrato che riprova la transazione se si verifica un'eccezione riprovabile (ad esempio in caso di timeout o conflitti). OCC

2.x

Il numero massimo di tentativi di nuovo tentativo e la strategia di backoff sono configurabili.

Il limite di tentativi predefinito è4, e la strategia di backoff predefinita è. DefaultQldbTransactionBackoffStrategy È possibile impostare la configurazione dei nuovi tentativi per istanza del driver e anche per transazione utilizzando un'istanza di. RetryPolicy

Il seguente esempio di codice specifica la logica di ripetizione con un limite di tentativi personalizzato e una strategia di backoff personalizzata per un'istanza del driver.

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()); } }

Il seguente esempio di codice specifica la logica dei tentativi con un limite di tentativi personalizzato e una strategia di backoff personalizzata per una particolare transazione. Questa configurazione execute sostituisce la logica di riprova impostata per l'istanza del driver.

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

Il numero massimo di tentativi di nuovo tentativo è configurabile. È possibile configurare il limite di tentativi impostando la retryLimit proprietà al momento dell'inizializzazione. PooledQldbDriver

Il limite di tentativi predefinito è. 4

Implementazione di vincoli di unicità

QLDBnon supporta indici univoci, ma puoi implementare questo comportamento nella tua applicazione.

Supponiamo di voler implementare un vincolo di unicità sul campo della tabella. GovId Person A tale scopo, è possibile scrivere una transazione che esegua le seguenti operazioni:

  1. Asserisce che la tabella non ha documenti esistenti con un valore specificatoGovId.

  2. Inserisci il documento se l'asserzione ha esito positivo.

Se una transazione concorrente supera contemporaneamente l'asserzione, solo una delle transazioni verrà salvata correttamente. L'altra transazione avrà esito negativo con un'OCCeccezione di conflitto.

Il seguente esempio di codice mostra come implementare questa logica di vincolo di unicità.

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); } });
Nota

In questo esempio, si consiglia di disporre di un indice sul GovId campo per ottimizzare le prestazioni. Senza un indice attivoGovId, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni di OCC conflitto o a timeout delle transazioni.

Lavorare con Amazon Ion

Esistono diversi modi per elaborare i dati di Amazon IonQLDB. Puoi utilizzare i metodi integrati della libreria Ion per creare e modificare i documenti in modo flessibile secondo necessità. In alternativa, puoi utilizzare il modulo XML di formato dati Jackson di Faster per Ion per mappare i documenti Ion su semplici vecchi modelli Java object (POJO).

Le sezioni seguenti forniscono esempi di codice di elaborazione dei dati Ion utilizzando entrambe le tecniche.

Importazione dei pacchetti Ion

Aggiungi l'artefatto ion-java come dipendenza nel tuo progetto 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>

Importa i seguenti pacchetti Ion.

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

Aggiungi l'artefatto jackson-dataformat-ioncome dipendenza nel tuo progetto Java. QLDBrichiede una versione 2.10.0 o successiva.

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>

Importa i seguenti pacchetti 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;

Inizializzazione di Ion

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

Creazione di oggetti Ion

Il seguente esempio di codice crea un oggetto Ion utilizzando l'IonStructinterfaccia e i relativi metodi incorporati.

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

Supponiamo di avere una classe modello JSON -mapped denominata come Person segue.

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; } }

Il seguente esempio di codice crea un IonStruct oggetto da un'istanza di. Person

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

Leggere oggetti Ion

Il seguente esempio di codice stampa ogni campo dell'ionStructistanza.

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

Il seguente esempio di codice legge un IonStruct oggetto e lo mappa su un'istanza di. 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

Per ulteriori informazioni sull'utilizzo di Ion, consulta la documentazione di Amazon Ion su GitHub. Per altri esempi di codice su come lavorare con Ion inQLDB, consultaUtilizzo dei tipi di dati Amazon Ion in Amazon QLDB.