

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à.

# Guida introduttiva al driver Amazon QLDB
Guida introduttiva al driver

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questo capitolo contiene tutorial pratici per aiutarti a imparare a sviluppare con Amazon QLDB utilizzando il driver QLDB. Il driver è basato sull' AWS SDK, che supporta l'interazione con l'API [QLDB](api-reference.md).

**Astrazione della sessione QLDB**

*Il driver fornisce un livello di astrazione di alto livello sopra l'API dei dati transazionali (sessione QLDB).* Semplifica il processo di esecuzione delle istruzioni [PartiQL](ql-reference.md) sui dati di registro gestendo [SendCommand](https://docs.aws.amazon.com/qldb/latest/developerguide/API_QLDB-Session_SendCommand.html)le chiamate API. Queste chiamate API richiedono diversi parametri che il driver gestisce automaticamente, tra cui la gestione delle sessioni, delle transazioni e la politica di riprova in caso di errori. Il driver offre anche ottimizzazioni delle prestazioni e applica le migliori pratiche per interagire con QLDB.

**Nota**  
Per interagire con le operazioni dell'API di gestione delle risorse elencate nel riferimento dell'[API Amazon QLDB](https://docs.aws.amazon.com/qldb/latest/developerguide/API_Operations_Amazon_QLDB.html), utilizzi direttamente l'SDK AWS anziché il driver. L'API di gestione viene utilizzata solo per la gestione delle risorse di registro e per le operazioni sui dati non transazionali, come l'esportazione, lo streaming e la verifica dei dati.

**Supporto Amazon Ion**

Inoltre, il driver utilizza le librerie [Amazon Ion](ion.md) per fornire supporto per la gestione dei dati Ion durante l'esecuzione delle transazioni. Queste librerie si occupano anche del calcolo dell'hash dei valori Ion. QLDB richiede questi hash ionici per verificare l'integrità delle richieste di transazione di dati.

**Terminologia dei driver**

Questo strumento è chiamato *driver* perché è paragonabile ad altri driver di database che forniscono interfacce facili da usare per gli sviluppatori. Analogamente, questi driver incapsulano la logica che converte un set standard di comandi e funzioni in chiamate specifiche richieste dall'API di basso livello del servizio.

Il driver è open source GitHub ed è disponibile per i seguenti linguaggi di programmazione:
+ [Driver Java](getting-started.java.md)
+ [driver DI.NET](getting-started.dotnet.md)
+ [Vai all'autista](getting-started.golang.md)
+ [Driver Node.js](getting-started.nodejs.md)
+ [Driver Python](getting-started.python.md)

Per informazioni generali sui driver per tutti i linguaggi di programmazione supportati e tutorial aggiuntivi, consulta i seguenti argomenti:
+ [Gestione delle sessioni con il driver](driver-session-management.md)
+ [Consigli per i conducenti](driver.best-practices.md)
+ [Politica sui nuovi tentativi del conducente](driver-retry-policy.md)
+ [Errori comuni](driver-errors.md)
+ [Esempio di tutorial applicativo](getting-started-sample-app.md)
+ [Lavorare con Amazon Ion](driver-working-with-ion.md)
+ [Ottenere statistiche sulle istruzioni PartiQL](working.statement-stats.md)

# Driver Amazon QLDB per Java
Driver Java

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Per utilizzare i dati nel registro, puoi connetterti ad Amazon QLDB dall'applicazione Java utilizzando un driver fornito. AWS I seguenti argomenti descrivono come iniziare a usare il driver QLDB per Java.

**Topics**
+ [

## Risorse per i driver
](#getting-started.java.resources)
+ [

## Prerequisiti
](#getting-started.java.prereqs)
+ [

## Impostazione delle AWS credenziali e della regione predefinite
](#getting-started.java.credentials)
+ [

## Installazione
](#getting-started.java.install)
+ [Tutorial di avvio rapido](driver-quickstart-java.md)
+ [Riferimento al libro di cucina](driver-cookbook-java.md)

## Risorse per i driver


Per ulteriori informazioni sulle funzionalità supportate dal driver Java, consultate le seguenti risorse:
+ [Riferimento API: [2.x, 1.x](https://javadoc.io/doc/software.amazon.qldb/amazon-qldb-driver-java/latest/)](https://javadoc.io/doc/software.amazon.qldb/amazon-qldb-driver-java/1.1.0/index.html)
+ [Codice sorgente del driver () GitHub](https://github.com/awslabs/amazon-qldb-driver-java)
+ [Codice sorgente dell'applicazione di esempio (GitHub)](https://github.com/aws-samples/amazon-qldb-dmv-sample-java)
+ [Framework Ledger Loader () GitHub](https://github.com/awslabs/amazon-qldb-ledger-load-java)
+ [Esempi di codice Amazon Ion](ion.code-examples.md)

## Prerequisiti


Prima di iniziare a utilizzare il driver QLDB per Java, è necessario effettuare le seguenti operazioni:

1. Segui le istruzioni di AWS configurazione riportate in. [Accesso ad Amazon QLDB](accessing.md) Questo include gli output seguenti:

   1. Iscriviti a AWS.

   1. Crea un utente con le autorizzazioni QLDB appropriate.

   1. Concedi l'accesso programmatico per lo sviluppo.

1. Configura un ambiente di sviluppo Java scaricando e installando quanto segue:

   1. Kit di sviluppo Java SE 8, ad esempio [Amazon Corretto 8](https://docs.aws.amazon.com/corretto/latest/corretto-8-ug/downloads-list.html).

   1. (Facoltativo) Ambiente di sviluppo integrato Java (IDE) a scelta, come [Eclipse](http://www.eclipse.org) o [IntelliJ](https://www.jetbrains.com/idea/).

1. Configura il tuo ambiente di sviluppo per il AWS SDK per Java momento. [Impostazione delle AWS credenziali e della regione predefinite](#getting-started.java.credentials)

Successivamente, puoi scaricare l'applicazione di esempio completa del tutorial oppure puoi installare solo il driver in un progetto Java ed eseguire brevi esempi di codice.
+ Per installare il driver QLDB e AWS SDK per Java poi in un progetto esistente, procedi con. [Installazione](#getting-started.java.install)
+ Per configurare un progetto ed eseguire brevi esempi di codice che illustrano le transazioni di dati di base su un registro, consulta il. [Tutorial di avvio rapido](driver-quickstart-java.md)
+ Per eseguire esempi più approfonditi delle operazioni delle API relative ai dati e alla gestione nell'applicazione di esempio completa del tutorial, consulta il. [Tutorial su Java](getting-started.java.tutorial.md)

## Impostazione delle AWS credenziali e della regione predefinite


Il driver QLDB e il [AWS SDK per Java](https://aws.amazon.com/sdk-for-java)sottostante richiedono l'immissione di credenziali all'applicazione AWS in fase di esecuzione. *Gli esempi di codice in questa guida presuppongono che tu stia utilizzando un file di AWS credenziali, come descritto in [Impostare credenziali e regione predefinite](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-credentials) nella Guida per gli sviluppatori.AWS SDK for Java 2.x *

Come parte di questi passaggi, è inoltre necessario impostare il valore predefinito Regione AWS per determinare l'endpoint QLDB predefinito. Gli esempi di codice si connettono a QLDB come impostazione predefinita. Regione AWS Per un elenco completo delle regioni in cui è disponibile QLDB, consulta gli endpoint e le quote di [Amazon QLDB](https://docs.aws.amazon.com/general/latest/gr/qldb.html) nel. *Riferimenti generali di AWS*

Di seguito è riportato un esempio di file di AWS credenziali denominato`~/.aws/credentials`, in cui il carattere tilde () rappresenta la tua home directory. `~`

```
[default] 
aws_access_key_id = your_access_key_id
aws_secret_access_key = your_secret_access_key
```

Sostituite i valori AWS delle vostre credenziali con i valori e. *your\$1access\$1key\$1id* *your\$1secret\$1access\$1key*

## Installazione


QLDB supporta le seguenti versioni dei driver Java e AWS le relative dipendenze SDK.


****  

| Versione driver | AWS SDK | Stato | Data di rilascio più recente | 
| --- | --- | --- | --- | 
| [1.x](https://search.maven.org/artifact/software.amazon.qldb/amazon-qldb-driver-java/1.1.0/jar) | AWS SDK per Java 1.x | Rilascio di produzione | 20 marzo 2020 | 
| [2.x](https://search.maven.org/artifact/software.amazon.qldb/amazon-qldb-driver-java/2.3.1/jar) | AWS SDK for Java 2.x | Versione di produzione | 4 giugno 2021 | 

Per installare il driver QLDB, consigliamo di utilizzare un sistema di gestione delle dipendenze, come Gradle o Maven. Ad esempio, aggiungi il seguente elemento come dipendenza nel tuo progetto Java.

------
#### [ 2.x ]

**Gradle**

Aggiungi questa dipendenza nel tuo `build.gradle` file di configurazione.

```
dependencies {
    compile group: 'software.amazon.qldb', name: 'amazon-qldb-driver-java', version: '2.3.1'
}
```

**Maven**

Aggiungi questa dipendenza nel tuo `pom.xml` file di configurazione.

```
<dependencies>
  <dependency>
    <groupId>software.amazon.qldb</groupId>
    <artifactId>amazon-qldb-driver-java</artifactId>
    <version>2.3.1</version>
  </dependency>
</dependencies>
```

Questo elemento include automaticamente il modulo AWS SDK for Java 2.x principale, le librerie [Amazon Ion](ion.md) e altre dipendenze richieste.

------
#### [ 1.x ]

**Gradle**

Aggiungi questa dipendenza nel tuo `build.gradle` file di configurazione.

```
dependencies {
    compile group: 'software.amazon.qldb', name: 'amazon-qldb-driver-java', version: '1.1.0'
}
```

**Maven**

Aggiungi questa dipendenza nel tuo `pom.xml` file di configurazione.

```
<dependencies>
  <dependency>
    <groupId>software.amazon.qldb</groupId>
    <artifactId>amazon-qldb-driver-java</artifactId>
    <version>1.1.0</version>
  </dependency>
</dependencies>
```

Questo elemento include automaticamente il modulo AWS SDK per Java principale, le librerie [Amazon Ion](ion.md) e altre dipendenze richieste.

**Importante**  
Spazio **dei nomi Amazon Ion**: quando importi classi Amazon Ion nella tua applicazione, devi utilizzare il pacchetto che si trova nello spazio dei nomi. `com.amazon.ion` AWS SDK per Java Dipende da un altro pacchetto Ion nello spazio dei nomi`software.amazon.ion`, ma si tratta di un pacchetto legacy non compatibile con il driver QLDB.

------

Per brevi esempi di codice su come eseguire transazioni di dati di base su un registro, consulta il. [Riferimento al libro di cucina](driver-cookbook-java.md)

### Altre librerie opzionali


Facoltativamente, puoi anche aggiungere le seguenti utili librerie al tuo progetto. Questi artefatti sono dipendenze obbligatorie nell'applicazione di esempio. [Tutorial su Java](getting-started.java.tutorial.md)

1. [aws-java-sdk-qldb](https://search.maven.org/artifact/com.amazonaws/aws-java-sdk-qldb/1.11.785/jar)— Il modulo QLDB di. AWS SDK per Java La versione minima supportata da QLDB è. `1.11.785`

   Utilizza questo modulo nella tua applicazione per interagire direttamente con le operazioni dell'API di gestione elencate in. [Riferimento all'API Amazon QLDB](api-reference.md)

1. [jackson-dataformat-ion](https://search.maven.org/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-ion/2.10.0/bundle)— Il modulo di formato dati Jackson di FasterXML per Ion. L'applicazione di esempio richiede una versione `2.10.0` o successiva.

------
#### [ Gradle ]

Aggiungi queste dipendenze nel tuo file `build.gradle` di configurazione.

```
dependencies {
    compile group: 'com.amazonaws', name: 'aws-java-sdk-qldb', version: '1.11.785'
    compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-ion', version: '2.10.0'
}
```

------
#### [ Maven ]

Aggiungi queste dipendenze nel tuo `pom.xml` file di configurazione.

```
<dependencies>
  <dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-qldb</artifactId>
    <version>1.11.785</version>
  </dependency>
  <dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-ion</artifactId>
    <version>2.10.0</version>
  </dependency>
</dependencies>
```

------

# Driver Amazon QLDB per Java — Tutorial di avvio rapido
Tutorial di avvio rapido

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo tutorial, imparerai come configurare una semplice applicazione utilizzando l'ultima versione del driver Amazon QLDB per Java. Questa guida include i passaggi per l'installazione del driver e brevi esempi in codice delle operazioni di base di *creazione, lettura, aggiornamento ed eliminazione* (CRUD). Per esempi più approfonditi che illustrano queste operazioni in un'applicazione di esempio completa, consulta la. [Tutorial su Java](getting-started.java.tutorial.md)

**Topics**
+ [

## Prerequisiti
](#driver-quickstart-java.prereqs)
+ [

## Fase 1: Configurazione del progetto
](#driver-quickstart-java.step-1)
+ [

## Fase 2: Inizializzare il driver
](#driver-quickstart-java.step-2)
+ [

## Fase 3: Creare una tabella e un indice
](#driver-quickstart-java.step-3)
+ [

## Fase 4: Inserimento di un documento
](#driver-quickstart-java.step-4)
+ [

## Fase 5: Interrogare il documento
](#driver-quickstart-java.step-5)
+ [

## Fase 6: Aggiornare il documento
](#driver-quickstart-java.step-6)
+ [

## Esecuzione dell'applicazione completa
](#driver-quickstart-java.complete)

## Prerequisiti


Prima di iniziare, assicurati di fare quanto segue:

1. Completa il driver [Prerequisiti](getting-started.java.md#getting-started.java.prereqs) per il Java, se non l'hai già fatto. Ciò include la registrazione AWS, la concessione dell'accesso programmatico per lo sviluppo e l'installazione di un ambiente di sviluppo integrato (IDE) Java.

1. Crea un libro mastro denominato. `quick-start`

   Per informazioni su come creare un libro mastro, consulta [Operazioni di base per i registri Amazon QLDB](ledger-management.basics.md) o [Fase 1: Creare un nuovo libro contabile](getting-started-step-1.md) in *Guida introduttiva alla console*.

## Fase 1: Configurazione del progetto


Innanzitutto, configura il tuo progetto Java. Ti consigliamo di utilizzare il sistema di gestione delle dipendenze [Maven](https://maven.apache.org/index.html) per questo tutorial.

**Nota**  
Se utilizzi un IDE con funzionalità per automatizzare questi passaggi di configurazione, puoi passare direttamente a. [Fase 2: Inizializzare il driver](#driver-quickstart-java.step-2)

1. Crea una cartella per la tua applicazione.

   ```
   $ mkdir myproject
   $ cd myproject
   ```

1. Inserisci il seguente comando per inizializzare il tuo progetto da un modello Maven. Sostituisci *project-package* e *maven-template* con i tuoi valori*project-name*, a seconda dei casi.

   ```
   $ mvn archetype:generate
     -DgroupId=project-package \
     -DartifactId=project-name \
     -DarchetypeArtifactId=maven-template \
     -DinteractiveMode=false
   ```

   Per *maven-template* esempio, puoi usare il modello Maven di base: `maven-archetype-quickstart`

1. Per aggiungere il [driver QLDB per](https://search.maven.org/artifact/software.amazon.qldb/amazon-qldb-driver-java/2.3.1/jar) Java come dipendenza del progetto, accedi al file appena `pom.xml` creato e aggiungi il seguente elemento.

   ```
   <dependency>
       <groupId>software.amazon.qldb</groupId>
       <artifactId>amazon-qldb-driver-java</artifactId>
       <version>2.3.1</version>
   </dependency>
   ```

   Questo elemento include automaticamente il modulo [AWS SDK for Java 2.x](https://aws.amazon.com/sdk-for-java)principale, le librerie [Amazon Ion](ion.md) e altre dipendenze richieste. Il tuo `pom.xml` file dovrebbe ora avere un aspetto simile al seguente.

   ```
   <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>software.amazon.qldb</groupId>
     <artifactId>qldb-quickstart</artifactId>
     <packaging>jar</packaging>
     <version>1.0-SNAPSHOT</version>
     <name>qldb-quickstart</name>
     <url>http://maven.apache.org</url>
     <dependencies>
       <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>3.8.1</version>
         <scope>test</scope>
       </dependency>
       <dependency>
           <groupId>software.amazon.qldb</groupId>
           <artifactId>amazon-qldb-driver-java</artifactId>
           <version>2.3.1</version>
       </dependency>
     </dependencies>
   </project>
   ```

1. Apri il file `App.java`.

   Quindi, aggiungi in modo incrementale gli esempi di codice nei passaggi seguenti per provare alcune operazioni CRUD di base. [In alternativa, puoi saltare il step-by-step tutorial ed eseguire invece l'applicazione completa.](#driver-quickstart-java.complete)

## Fase 2: Inizializzare il driver


Inizializza un'istanza del driver che si connette al registro denominato. `quick-start` Aggiungi il codice seguente al tuo `App.java` file.

```
import java.util.*;
import com.amazon.ion.*;
import com.amazon.ion.system.*;
import software.amazon.awssdk.services.qldbsession.QldbSessionClient;
import software.amazon.qldb.*;

public final class App {
    public static IonSystem ionSys = IonSystemBuilder.standard().build();
    public static QldbDriver qldbDriver;

    public static void main(final String... args) {
        System.out.println("Initializing the driver");
        qldbDriver = QldbDriver.builder()
            .ledger("quick-start")
            .transactionRetryPolicy(RetryPolicy
                .builder()
                .maxRetries(3)
                .build())
            .sessionClientBuilder(QldbSessionClient.builder())
            .build();
    }
}
```

## Fase 3: Creare una tabella e un indice


Il seguente esempio di codice mostra come eseguire le `CREATE INDEX` istruzioni `CREATE TABLE` e le istruzioni.

Nel `main` metodo, aggiungete il codice seguente che crea una tabella denominata `People` e un indice per il `lastName` campo di tale tabella. [Gli indici](ql-reference.create-index.md) sono necessari per ottimizzare le prestazioni delle query e aiutano a limitare le eccezioni ai conflitti [OCC (Optimistic Concurrency Control)](concurrency.md).

```
// Create a table and an index in the same transaction
qldbDriver.execute(txn -> {
    System.out.println("Creating a table and an index");
    txn.execute("CREATE TABLE People");
    txn.execute("CREATE INDEX ON People(lastName)");
});
```

## Fase 4: Inserimento di un documento


Il seguente esempio di codice mostra come eseguire un'`INSERT`istruzione. QLDB supporta il linguaggio di interrogazione [PartiQL](ql-reference.md) (compatibile con SQL) e il formato dati [Amazon](ion.md) Ion (superset di JSON).

Aggiungi il codice seguente che inserisce un documento nella tabella. `People`

```
// Insert a document
qldbDriver.execute(txn -> {
    System.out.println("Inserting a document");
    IonStruct person = ionSys.newEmptyStruct();
    person.put("firstName").newString("John");
    person.put("lastName").newString("Doe");
    person.put("age").newInt(32);
    txn.execute("INSERT INTO People ?", person);
});
```

Questo esempio utilizza un punto interrogativo (`?`) come segnaposto variabile per passare le informazioni del documento all'istruzione. Quando si utilizzano i segnaposto, è necessario passare un valore di tipo. `IonValue`

**Suggerimento**  
Per inserire più documenti utilizzando una sola [INSERT](ql-reference.insert.md) istruzione, potete passare un parametro di tipo [IonList](driver-working-with-ion.md#driver-ion-list)(espresso esplicitamente come un`IonValue`) all'istruzione nel modo seguente.  

```
// 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.*

## Fase 5: Interrogare il documento


Il seguente esempio di codice mostra come eseguire un'`SELECT`istruzione.

Aggiungere il codice seguente per interrogare un documento dalla `People` tabella.

```
// Query the document
qldbDriver.execute(txn -> {
    System.out.println("Querying the table");
    Result result = txn.execute("SELECT * FROM People WHERE lastName = ?",
            ionSys.newString("Doe"));
    IonStruct person = (IonStruct) result.iterator().next();
    System.out.println(person.get("firstName")); // prints John
    System.out.println(person.get("lastName")); // prints Doe
    System.out.println(person.get("age")); // prints 32
});
```

## Fase 6: Aggiornare il documento


Il seguente esempio di codice mostra come eseguire un'`UPDATE`istruzione.

1. Aggiungere il codice seguente che aggiorna un documento nella `People` tabella eseguendo l'aggiornamento `age` a`42`.

   ```
   // Update the document
   qldbDriver.execute(txn -> {
       System.out.println("Updating the document");
       final List<IonValue> parameters = new ArrayList<>();
       parameters.add(ionSys.newInt(42));
       parameters.add(ionSys.newString("Doe"));
       txn.execute("UPDATE People SET age = ? WHERE lastName = ?", parameters);
   });
   ```

1. Interroga nuovamente il documento per vedere il valore aggiornato.

   ```
   // Query the updated document
   qldbDriver.execute(txn -> {
       System.out.println("Querying the table for the updated document");
       Result result = txn.execute("SELECT * FROM People WHERE lastName = ?",
               ionSys.newString("Doe"));
       IonStruct person = (IonStruct) result.iterator().next();
       System.out.println(person.get("firstName")); // prints John
       System.out.println(person.get("lastName")); // prints Doe
       System.out.println(person.get("age")); // prints 32
   });
   ```

1. Usa Maven o il tuo IDE per compilare ed eseguire il file. `App.java`

## Esecuzione dell'applicazione completa


Il seguente esempio di codice è la versione completa dell'`App.java`applicazione. Invece di eseguire i passaggi precedenti singolarmente, potete anche copiare ed eseguire questo esempio di codice dall'inizio alla fine. Questa applicazione dimostra alcune operazioni CRUD di base sul registro denominato. `quick-start`

**Nota**  
Prima di eseguire questo codice, assicuratevi di non avere già una tabella attiva denominata `People` nel registro. `quick-start`  
Nella prima riga, sostituiscila *project-package* con il `groupId` valore che hai usato per il comando Maven in. [Fase 1: Configurazione del progetto](#driver-quickstart-java.step-1)

```
package project-package;

import java.util.*;
import com.amazon.ion.*;
import com.amazon.ion.system.*;
import software.amazon.awssdk.services.qldbsession.QldbSessionClient;
import software.amazon.qldb.*;

public class App {
    public static IonSystem ionSys = IonSystemBuilder.standard().build();
    public static QldbDriver qldbDriver;

    public static void main(final String... args) {
        System.out.println("Initializing the driver");
        qldbDriver = QldbDriver.builder()
            .ledger("quick-start")
            .transactionRetryPolicy(RetryPolicy
                .builder()
                .maxRetries(3)
                .build())
            .sessionClientBuilder(QldbSessionClient.builder())
            .build();

        // Create a table and an index in the same transaction
        qldbDriver.execute(txn -> {
            System.out.println("Creating a table and an index");
            txn.execute("CREATE TABLE People");
            txn.execute("CREATE INDEX ON People(lastName)");
        });
        
        // Insert a document
        qldbDriver.execute(txn -> {
            System.out.println("Inserting a document");
            IonStruct person = ionSys.newEmptyStruct();
            person.put("firstName").newString("John");
            person.put("lastName").newString("Doe");
            person.put("age").newInt(32);
            txn.execute("INSERT INTO People ?", person);
        });
        
        // Query the document
        qldbDriver.execute(txn -> {
            System.out.println("Querying the table");
            Result result = txn.execute("SELECT * FROM People WHERE lastName = ?",
                    ionSys.newString("Doe"));
            IonStruct person = (IonStruct) result.iterator().next();
            System.out.println(person.get("firstName")); // prints John
            System.out.println(person.get("lastName")); // prints Doe
            System.out.println(person.get("age")); // prints 32
        });
        
        // Update the document
        qldbDriver.execute(txn -> {
            System.out.println("Updating the document");
            final List<IonValue> parameters = new ArrayList<>();
            parameters.add(ionSys.newInt(42));
            parameters.add(ionSys.newString("Doe"));
            txn.execute("UPDATE People SET age = ? WHERE lastName = ?", parameters);
        });
        
        // Query the updated document
        qldbDriver.execute(txn -> {
            System.out.println("Querying the table for the updated document");
            Result result = txn.execute("SELECT * FROM People WHERE lastName = ?",
                    ionSys.newString("Doe"));
            IonStruct person = (IonStruct) result.iterator().next();
            System.out.println(person.get("firstName")); // prints John
            System.out.println(person.get("lastName")); // prints Doe
            System.out.println(person.get("age")); // prints 42
        });
    }
}
```

Usa Maven o il tuo IDE per compilare ed eseguire il file. `App.java`

# Driver Amazon QLDB per Java: riferimento al libro di cucina
Riferimento al libro di cucina

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questa guida di riferimento mostra i casi d'uso comuni del driver Amazon QLDB 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 QLDB per Java.

**Contents**
+ [

## Importazione del driver
](#cookbook-java.importing)
+ [

## Istanziazione del driver
](#cookbook-java.instantiating)
+ [

## Operazioni CRUD
](#cookbook-java.crud)
  + [

### Creazione di tabelle
](#cookbook-java.crud.creating-tables)
  + [

### Creazione di indici
](#cookbook-java.crud.creating-indexes)
  + [

### Leggere documenti
](#cookbook-java.crud.reading)
  + [

### Inserimento di documenti
](#cookbook-java.crud.inserting)
    + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-java.crud.inserting.multiple)
  + [

### Aggiornamento dei documenti
](#cookbook-java.crud.updating)
  + [

### Eliminazione di documenti
](#cookbook-java.crud.deleting)
  + [

### Esecuzione di più istruzioni in una transazione
](#cookbook-java.crud.multi-statement)
  + [

### Logica di ripetizione dei tentativi
](#cookbook-java.crud.retry-logic)
  + [

### Implementazione di vincoli di unicità
](#cookbook-java.crud.uniqueness-constraints)
+ [

## Lavorare con Amazon Ion
](#cookbook-java.ion)
  + [

### Importazione dei pacchetti Ion
](#cookbook-java.ion.importing)
  + [

### Inizializzazione di Ion
](#cookbook-java.ion.initializing)
  + [

### Creazione di oggetti Ion
](#cookbook-java.ion.creating)
  + [

### Leggere oggetti Ion
](#cookbook-java.ion.reading)

## Importazione del driver


Il seguente esempio di codice importa il driver, il client di sessione QLDB, 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](#cookbook-java.crud.retry-logic) 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](#cookbook-java.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();
```

------

## Operazioni CRUD


QLDB *esegue operazioni di creazione, lettura, aggiornamento ed* eliminazione (CRUD) come parte di una transazione.

**avvertimento**  
Come best practice, 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 in 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 conflitti ottimistici di controllo della concorrenza (OCC).*

[Modello di concorrenza Amazon QLDB](concurrency.md)Per ulteriori informazioni su OCC, vedere.

**Transazioni create implicitamente**

Il metodo [QldbDriver.execute](https://javadoc.io/doc/software.amazon.qldb/amazon-qldb-driver-java/latest/software/amazon/qldb/QldbDriver.html) accetta una funzione lambda che riceve un'istanza di [Executor](https://javadoc.io/doc/software.amazon.qldb/amazon-qldb-driver-java/latest/software/amazon/qldb/Executor.html), che può essere utilizzata per eseguire istruzioni. L'`Executor`istanza 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 operazioni CRUD 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](#cookbook-java.ion).

**Contents**
+ [

### Creazione di tabelle
](#cookbook-java.crud.creating-tables)
+ [

### Creazione di indici
](#cookbook-java.crud.creating-indexes)
+ [

### Leggere documenti
](#cookbook-java.crud.reading)
+ [

### Inserimento di documenti
](#cookbook-java.crud.inserting)
  + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-java.crud.inserting.multiple)
+ [

### Aggiornamento dei documenti
](#cookbook-java.crud.updating)
+ [

### Eliminazione di documenti
](#cookbook-java.crud.deleting)
+ [

### Esecuzione di più istruzioni in una transazione
](#cookbook-java.crud.multi-statement)
+ [

### Logica di ripetizione dei tentativi
](#cookbook-java.crud.retry-logic)
+ [

### Implementazione di vincoli di unicità
](#cookbook-java.crud.uniqueness-constraints)

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

#### Utilizzo del mappatore Jackson


```
// 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](ql-reference.create-index.md) sul campo per ottimizzare le `GovId` prestazioni. Senza un indice attivo`GovId`, le query possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC 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);
    }
});
```

#### Utilizzo del mappatore Jackson


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 attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

#### Inserimento di più documenti in un'unica dichiarazione


Per inserire più documenti utilizzando una sola [INSERT](ql-reference.insert.md) istruzione, potete passare un parametro di tipo [IonList](driver-working-with-ion.md#driver-ion-list)(espresso esplicitamente come un`IonValue`) 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.*

##### Perché è richiesto il cast esplicito?


Il metodo [TransactionExecutor.execute](https://javadoc.io/doc/software.amazon.qldb/amazon-qldb-driver-java/latest/software/amazon/qldb/TransactionExecutor.html) è sovraccarico. Accetta un numero variabile di `IonValue` argomenti (*varargs*) o un singolo argomento. `List<IonValue>` In [ion-java](https://www.javadoc.io/doc/com.amazon.ion/ion-java/latest/index.html), `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);
});
```

#### Usando il mappatore Jackson


```
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 attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

### Eliminazione di documenti


```
qldbDriver.execute(txn -> {
    txn.execute("DELETE FROM Person WHERE GovId = ?",
            SYSTEM.newString("TOYENC486FH"));
});
```

#### Usando il mappatore Jackson


```
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 attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC 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](https://javadoc.io/doc/software.amazon.qldb/amazon-qldb-driver-java/latest/software/amazon/qldb/DefaultQldbTransactionBackoffStrategy.html) È possibile impostare la configurazione dei nuovi tentativi per istanza del driver e anche per transazione utilizzando un'istanza di. [RetryPolicy](https://javadoc.io/doc/software.amazon.qldb/amazon-qldb-driver-java/latest/software/amazon/qldb/RetryPolicy.html)

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à


QLDB non 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 specificato`GovId`.

1. 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'eccezione relativa al conflitto OCC.

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 attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

## Lavorare con Amazon Ion


Esistono diversi modi per elaborare i dati di Amazon Ion in QLDB. Puoi utilizzare i metodi integrati della [libreria Ion](https://github.com/amzn/ion-java) per creare e modificare i documenti in modo flessibile secondo necessità. In alternativa, puoi utilizzare il [modulo di formato dati Jackson di FasterXML per Ion per mappare i documenti Ion](https://github.com/FasterXML/jackson-dataformats-binary/tree/master/ion) su *semplici vecchi modelli di oggetti Java* (POJO).

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

**Contents**
+ [

### Importazione dei pacchetti Ion
](#cookbook-java.ion.importing)
+ [

### Inizializzazione di Ion
](#cookbook-java.ion.initializing)
+ [

### Creazione di oggetti Ion
](#cookbook-java.ion.creating)
+ [

### Leggere oggetti Ion
](#cookbook-java.ion.reading)

### Importazione dei pacchetti Ion


Aggiungi l'artefatto [ion-java](https://search.maven.org/artifact/com.amazon.ion/ion-java/1.6.1/bundle) 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;
```

#### Usando il mappatore Jackson


Aggiungi l'artefatto [jackson-dataformat-ion](https://search.maven.org/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-ion/2.10.0/bundle)come dipendenza nel tuo progetto Java. QLDB richiede `2.10.0` una versione 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();
```

#### Usando il mappatore Jackson


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

### Creazione di oggetti Ion


Il seguente esempio di codice crea un oggetto Ion utilizzando l'`IonStruct`interfaccia 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
```

#### Utilizzo del mappatore Jackson


Supponiamo di avere una classe modello mappata in JSON denominata come segue. `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;
    }
}
```

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'`ionStruct`istanza.

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

#### Utilizzo del mappatore Jackson


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](http://amzn.github.io/ion-docs/) su GitHub. Per altri esempi di codice sull'utilizzo di Ion in QLDB, vedere. [Utilizzo dei tipi di dati Amazon Ion in Amazon QLDB](driver-working-with-ion.md)

# Driver Amazon QLDB per.NET
driver DI.NET

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Per utilizzare i dati nel registro, puoi connetterti ad Amazon QLDB dall'applicazione Microsoft.NET utilizzando un driver fornito. AWS Il driver è indirizzato a **.NET Standard 2.0**. Più specificamente, supporta **.NET Core (LTS) 2.1\$1 **e.NET** Framework 4.5.2\$1**. Per informazioni sulla compatibilità, [vedi.NET Standard](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) nel sito *Microsoft Docs.*

Consigliamo vivamente di utilizzare lo strumento di *mappatura degli oggetti Ion per* evitare completamente la necessità di effettuare la conversione manuale tra i tipi di Amazon Ion e i tipi nativi di C\$1.

Negli argomenti seguenti viene descritto come iniziare a utilizzare il driver QLDB per.NET.

**Topics**
+ [

## Risorse per i driver
](#getting-started.dotnet.resources)
+ [

## Prerequisiti
](#getting-started.dotnet.prereqs)
+ [

## Installazione
](#getting-started.dotnet.install)
+ [Tutorial di avvio rapido](driver-quickstart-dotnet.md)
+ [Riferimento al libro di cucina](driver-cookbook-dotnet.md)

## Risorse per i driver


Per ulteriori informazioni sulle funzionalità supportate dal driver.NET, consulta le seguenti risorse:
+ [Documentazione di riferimento dell'API](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.html)
+ [Codice sorgente del driver (GitHub)](https://github.com/awslabs/amazon-qldb-driver-dotnet)
+ [Codice sorgente dell'applicazione di esempio (GitHub)](https://github.com/aws-samples/amazon-qldb-dmv-sample-dotnet)
+ [Ricettario Amazon Ion](http://amzn.github.io/ion-docs/guides/cookbook.html)
+ [Mappatore di oggetti Ion () GitHub](https://github.com/amzn/ion-object-mapper-dotnet)

## Prerequisiti


Prima di iniziare a utilizzare il driver QLDB per.NET, è necessario effettuare le seguenti operazioni:

1. Segui le istruzioni di AWS configurazione riportate in. [Accesso ad Amazon QLDB](accessing.md) Questo include gli output seguenti:

   1. Iscriviti a AWS.

   1. Crea un utente con le autorizzazioni QLDB appropriate.

   1. Concedi l'accesso programmatico per lo sviluppo.

1. Scarica e installa di.NET Core SDK versione 2.1 o successiva dal sito di [download di Microsoft.NET](https://dotnet.microsoft.com/download).

1. (Facoltativo) Installa un ambiente di sviluppo integrato (IDE) a tua scelta, come Visual Studio, Visual Studio per Mac o Visual Studio Code. È possibile scaricarli dal sito di [Microsoft Visual Studio](https://visualstudio.microsoft.com/).

1. Configura il tuo ambiente di sviluppo per [AWS SDK per .NET](https://aws.amazon.com/sdk-for-net):

   1. Configura le tue AWS credenziali. Ti consigliamo di creare un file di credenziali condiviso.

      *Per istruzioni, consulta [Configurazione delle AWS credenziali utilizzando un file di credenziali](https://docs.aws.amazon.com/sdk-for-net/latest/developer-guide/net-dg-config-creds.html#creds-file) nella Guida per gli sviluppatori.AWS SDK per .NET *

   1. Imposta il tuo valore predefinito. Regione AWS Per sapere come, vedi [Regione AWS selezione](https://docs.aws.amazon.com/sdk-for-net/latest/developer-guide/net-dg-region-selection.html).

      Per un elenco completo delle regioni disponibili, consulta gli [endpoint e le quote di Amazon QLDB](https://docs.aws.amazon.com/general/latest/gr/qldb.html) nel. *Riferimenti generali di AWS*

Successivamente, puoi configurare un'applicazione di esempio di base ed eseguire esempi di codice breve, oppure puoi installare il driver in un progetto.NET esistente.
+ Per installare il driver QLDB e AWS SDK per .NET poi in un progetto esistente, procedi con. [Installazione](#getting-started.dotnet.install)
+ Per configurare un progetto ed eseguire brevi esempi di codice che illustrano le transazioni di dati di base su un registro, consulta il. [Tutorial di avvio rapido](driver-quickstart-dotnet.md)

## Installazione


Usa il gestore di NuGet pacchetti per installare il driver QLDB per.NET. Ti consigliamo di utilizzare Visual Studio o un IDE a tua scelta per aggiungere dipendenze al progetto. Il nome del pacchetto driver è [Amazon.QLDB.Driver](https://www.nuget.org/packages/amazon.qldb.driver).

Ad esempio, in Visual Studio, apri la **console NuGet Package Manager** nel menu **Strumenti**. Quindi, inserisci il seguente comando al `PM>` prompt.

```
PM> Install-Package Amazon.QLDB.Driver
```

L'installazione del driver installa anche le sue dipendenze, inclusi i pacchetti Amazon [Ion e AWS SDK per .NET Amazon.](ion.md)

### Installa lo strumento di mappatura degli oggetti Ion


La versione 1.3.0 del driver QLDB per.NET introduce il supporto per l'accettazione e la restituzione di tipi di dati C\$1 nativi senza la necessità di utilizzare Amazon Ion. Per utilizzare questa funzionalità, aggiungi il seguente pacchetto al tuo progetto.
+ [Amazon.qldb.driver.Serialization](https://www.nuget.org/packages/Amazon.QLDB.Driver.Serialization/): una libreria in grado di mappare i valori Ion su *semplici vecchi oggetti CLR* (POCO) di C\$1 e viceversa. Questo mappatore di oggetti Ion consente all'applicazione di interagire direttamente con i tipi di dati nativi C\$1 senza la necessità di lavorare con Ion. Per una breve guida su come utilizzare questa libreria, consultate il file [Serialization.md](https://github.com/awslabs/amazon-qldb-driver-dotnet/blob/master/SERIALIZATION.md) nel repository. GitHub `awslabs/amazon-qldb-driver-dotnet`

Per installare questo pacchetto, inserisci il seguente comando.

```
PM> Install-Package Amazon.QLDB.Driver.Serialization
```

Per brevi esempi di codice su come eseguire transazioni di dati di base su un registro, vedere il[Riferimento al libro di cucina](driver-cookbook-dotnet.md).

# Driver Amazon QLDB per.NET — Tutorial di avvio rapido
Tutorial di avvio rapido

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo tutorial, imparerai come configurare una semplice applicazione utilizzando il driver Amazon QLDB per.NET. Questa guida include i passaggi per l'installazione del driver e brevi esempi di operazioni di base di *creazione, lettura, aggiornamento ed eliminazione* (CRUD).

**Topics**
+ [

## Prerequisiti
](#driver-quickstart-dotnet.prereqs)
+ [

## Fase 1: Configurazione del progetto
](#driver-quickstart-dotnet.step-1)
+ [

## Fase 2: Inizializzare il driver
](#driver-quickstart-dotnet.step-2)
+ [

## Fase 3: Creare una tabella e un indice
](#driver-quickstart-dotnet.step-3)
+ [

## Fase 4: Inserimento di un documento
](#driver-quickstart-dotnet.step-4)
+ [

## Fase 5: Interrogare il documento
](#driver-quickstart-dotnet.step-5)
+ [

## Fase 6: Aggiornare il documento
](#driver-quickstart-dotnet.step-6)
+ [

## Esecuzione dell'applicazione completa
](#driver-quickstart-dotnet.complete)

## Prerequisiti


Prima di iniziare, assicuratevi di fare quanto segue:

1. Completa il driver [Prerequisiti](getting-started.dotnet.md#getting-started.dotnet.prereqs) for the .NET, se non l'hai già fatto. Ciò include la registrazione AWS, la concessione dell'accesso programmatico per lo sviluppo e l'installazione di.NET Core SDK.

1. Crea un libro mastro denominato. `quick-start`

   Per informazioni su come creare un registro, consulta [Operazioni di base per i registri Amazon QLDB](ledger-management.basics.md) o [Fase 1: Creare un nuovo libro contabile](getting-started-step-1.md) in *Guida introduttiva alla console*.

## Fase 1: Configurazione del progetto


Innanzitutto, configura il tuo progetto.NET.

1. Per creare ed eseguire un'applicazione modello, inserisci i seguenti `dotnet` comandi su un terminale come *bash* o *Command Prompt*. *PowerShell*

   ```
   $ dotnet new console --output Amazon.QLDB.QuickStartGuide
   $ dotnet run --project Amazon.QLDB.QuickStartGuide
   ```

   Questo modello crea una cartella denominata. `Amazon.QLDB.QuickStartGuide` In quella cartella, crea un progetto con lo stesso nome e un file denominato`Program.cs`. Il programma contiene codice che visualizza l'output`Hello World!`.

1. Usa il gestore di NuGet pacchetti per installare il driver QLDB per.NET. Ti consigliamo di utilizzare Visual Studio o un IDE a tua scelta per aggiungere dipendenze al tuo progetto. Il nome del pacchetto driver è [Amazon.QLDB.Driver](https://www.nuget.org/packages/amazon.qldb.driver).
   + Ad esempio, in Visual Studio, apri la **console NuGet Package Manager** nel menu **Strumenti**. Quindi, inserisci il seguente comando al `PM>` prompt.

     ```
     PM> Install-Package Amazon.QLDB.Driver
     ```
   + In alternativa, puoi inserire i seguenti comandi sul tuo terminale.

     ```
     $ cd Amazon.QLDB.QuickStartGuide
     $ dotnet add package Amazon.QLDB.Driver
     ```

   L'installazione del driver installa anche le sue dipendenze, incluse le librerie Amazon [Ion e [AWS SDK per .NET](https://aws.amazon.com/sdk-for-net)Amazon.](ion.md)

1. Installa la libreria di serializzazione del driver.

   ```
   PM> Install-Package Amazon.QLDB.Driver.Serialization
   ```

1. Apri il file `Program.cs`.

   Quindi, aggiungi in modo incrementale gli esempi di codice nei passaggi seguenti per provare alcune operazioni CRUD di base. [In alternativa, puoi saltare il step-by-step tutorial ed eseguire invece l'applicazione completa.](#driver-quickstart-dotnet.complete)

**Nota**  
**Scelta tra sincrono e asincrono APIs: il driver fornisce funzionalità sincrone** e asincrone. APIs Per le applicazioni ad alta richiesta che gestiscono più richieste senza blocchi, consigliamo di utilizzare la modalità asincrona per migliorare le prestazioni. APIs Il driver offre la funzionalità sincrona APIs come ulteriore comodità per le basi di codice esistenti scritte in modo sincrono.  
Questo tutorial include esempi di codice sincrono e asincrono. Per ulteriori informazioni su APIs, consulta il [IQldbDriver](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.IQldbDriver.html) e le [IAsyncQldbDriver](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.IAsyncQldbDriver.html)interfacce nella documentazione dell'API.
**Elaborazione dei dati Amazon Ion**: questo tutorial fornisce esempi di codice di elaborazione dei dati Amazon Ion utilizzando l'[object mapper Ion per](https://github.com/amzn/ion-object-mapper-dotnet) impostazione predefinita. QLDB ha introdotto il mappatore di oggetti Ion nella versione 1.3.0 del driver.NET. Ove applicabile, questo tutorial fornisce anche esempi di codice che utilizzano la [libreria Ion](https://github.com/amzn/ion-dotnet) standard come alternativa. Per ulteriori informazioni, consulta [Lavorare con Amazon Ion](driver-cookbook-dotnet.md#cookbook-dotnet.ion).

## Fase 2: Inizializzare il driver


Inizializza un'istanza del driver che si connette al registro denominato. `quick-start` Aggiungi il codice seguente al tuo `Program.cs` file.

------
#### [ Async ]

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        public class Person
        {
            public string FirstName { get; set; }

            public string LastName { get; set; }

            public int Age { get; set; }

            public override string ToString()
            {
                return FirstName + ", " + LastName + ", " + Age.ToString();
            }
        }

        static async Task Main(string[] args)
        {
            Console.WriteLine("Create the async QLDB driver");
            IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
                .WithLedger("quick-start")
                .WithSerializer(new ObjectSerializer())
                .Build();
        }
    }
}
```

------
#### [ Sync ]

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        public class Person
        {
            public string FirstName { get; set; }

            public string LastName { get; set; }

            public int Age { get; set; }

            public override string ToString()
            {
                return FirstName + ", " + LastName + ", " + Age.ToString();
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Create the sync QLDB driver");
            IQldbDriver driver = QldbDriver.Builder()
                .WithLedger("quick-start")
                .WithSerializer(new ObjectSerializer())
                .Build();
        }
    }
}
```

------

### Utilizzo della libreria Ion


------
#### [ Async ]

```
using System;
using System.Threading.Tasks;
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        static IValueFactory valueFactory = new ValueFactory();

        static async Task Main(string[] args)
        {
            Console.WriteLine("Create the async QLDB driver");
            IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
                .WithLedger("quick-start")
                .Build();
        }
    }
}
```

------
#### [ Sync ]

```
using System;
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;
using Amazon.QLDB.Driver;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        static IValueFactory valueFactory = new ValueFactory();

        static void Main(string[] args)
        {
            Console.WriteLine("Create the sync QLDB Driver");
            IQldbDriver driver = QldbDriver.Builder()
                .WithLedger("quick-start")
                .Build();
        }
    }
}
```

------

## Fase 3: Creare una tabella e un indice


Per il resto di questo tutorial fino al *Passaggio 6*, è necessario aggiungere i seguenti esempi di codice all'esempio di codice precedente.

In questo passaggio, il codice seguente mostra come eseguire le `CREATE INDEX` istruzioni `CREATE TABLE` e le istruzioni. Crea una tabella denominata `Person` e un indice per il `firstName` campo in quella tabella. [Gli indici](ql-reference.create-index.md) sono necessari per ottimizzare le prestazioni delle query e contribuire a limitare le eccezioni ai conflitti [OCC (Optimistic Concurrency Control)](concurrency.md).

------
#### [ Async ]

```
Console.WriteLine("Creating the table and index");

// Creates the table and the index in the same transaction.
// Note: Any code within the lambda can potentially execute multiple times due to retries.
// For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
await driver.Execute(async txn =>
{
    await txn.Execute("CREATE TABLE Person");
    await txn.Execute("CREATE INDEX ON Person(firstName)");
});
```

------
#### [ Sync ]

```
Console.WriteLine("Creating the tables and index");

// Creates the table and the index in the same transaction.
// Note: Any code within the lambda can potentially execute multiple times due to retries.
// For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
driver.Execute(txn =>
{
    txn.Execute("CREATE TABLE Person");
    txn.Execute("CREATE INDEX ON Person(firstName)");
});
```

------

## Fase 4: Inserimento di un documento


Il seguente esempio di codice mostra come eseguire un'`INSERT`istruzione. QLDB supporta il linguaggio di interrogazione [PartiQL](ql-reference.md) (compatibile con SQL) e il formato dati [Amazon](ion.md) Ion (superset di JSON).

Aggiungi il codice seguente che inserisce un documento nella tabella. `Person`

------
#### [ Async ]

```
Console.WriteLine("Inserting a document");

Person myPerson = new Person {
    FirstName = "John",
    LastName = "Doe",
    Age = 32
};

await driver.Execute(async txn =>
{
    IQuery<Person> myQuery = txn.Query<Person>("INSERT INTO Person ?", myPerson);
    await txn.Execute(myQuery);
});
```

------
#### [ Sync ]

```
Console.WriteLine("Inserting a document");

Person myPerson = new Person {
    FirstName = "John",
    LastName = "Doe",
    Age = 32
};

driver.Execute(txn =>
{
    IQuery<Person> myQuery = txn.Query<Person>("INSERT INTO Person ?", myPerson);
    txn.Execute(myQuery);
});
```

------

### Utilizzo della libreria Ion


------
#### [ Async ]

```
Console.WriteLine("Inserting a document");

// This is one way of creating Ion values. We can also use an IonLoader.
// For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("firstName", valueFactory.NewString("John"));
ionPerson.SetField("lastName", valueFactory.NewString("Doe"));
ionPerson.SetField("age", valueFactory.NewInt(32));

await driver.Execute(async txn =>
{
    await txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------
#### [ Sync ]

```
Console.WriteLine("Inserting a document");

// This is one way of creating Ion values, we can also use an IonLoader.
// For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("firstName", valueFactory.NewString("John"));
ionPerson.SetField("lastName", valueFactory.NewString("Doe"));
ionPerson.SetField("age", valueFactory.NewInt(32));

driver.Execute(txn =>
{
    txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------

**Suggerimento**  
Per inserire più documenti utilizzando una singola [INSERT](ql-reference.insert.md) istruzione, è possibile passare un parametro di tipo [Ion list](driver-working-with-ion.md#driver-ion-list) all'istruzione come segue.  

```
// people is an Ion list
txn.Execute("INSERT INTO Person ?", people);
```
Non racchiudete la variabile placeholder (`?`) tra parentesi angolari doppie (`<<...>>`) quando passate un elenco Ion. *Nelle istruzioni PartiQL manuali, le parentesi doppie angolari indicano una raccolta non ordinata nota come borsa.*

## Fase 5: Interrogare il documento


Il seguente esempio di codice mostra come eseguire un'`SELECT`istruzione.

Aggiungere il codice seguente per interrogare un documento dalla `Person` tabella.

------
#### [ Async ]

```
Console.WriteLine("Querying the table");

// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IAsyncResult<Person> selectResult = await driver.Execute(async txn =>
{
    IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
    return await txn.Execute(myQuery);
});

await foreach (Person person in selectResult)
{
    Console.WriteLine(person);
    // John, Doe, 32
}
```

------
#### [ Sync ]

```
Console.WriteLine("Querying the table");

// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IResult<Person> selectResult = driver.Execute(txn =>
{
    IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
    return txn.Execute(myQuery);
});

foreach (Person person in selectResult)
{
    Console.WriteLine(person);
    // John, Doe, 32
}
```

------

### Utilizzo della libreria Ion


------
#### [ Async ]

```
Console.WriteLine("Querying the table");

IIonValue ionFirstName = valueFactory.NewString("John");

// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IAsyncResult selectResult = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName);
});

await foreach (IIonValue row in selectResult)
{
    Console.WriteLine(row.GetField("firstName").StringValue);
    Console.WriteLine(row.GetField("lastName").StringValue);
    Console.WriteLine(row.GetField("age").IntValue);
}
```

------
#### [ Sync ]

```
Console.WriteLine("Querying the table");

IIonValue ionFirstName = valueFactory.NewString("John");

// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IResult selectResult = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName);
});

foreach (IIonValue row in selectResult)
{
    Console.WriteLine(row.GetField("firstName").StringValue);
    Console.WriteLine(row.GetField("lastName").StringValue);
    Console.WriteLine(row.GetField("age").IntValue);
}
```

------

Questo esempio utilizza un punto interrogativo (`?`) come segnaposto variabile per passare le informazioni del documento all'istruzione. Quando si utilizzano i segnaposto, è necessario passare un valore di tipo. `IonValue`

## Fase 6: Aggiornare il documento


Il seguente esempio di codice mostra come eseguire un'`UPDATE`istruzione.

1. Aggiungere il codice seguente che aggiorna un documento nella `Person` tabella aggiornandolo `age` alla versione 42.

------
#### [ Async ]

   ```
   Console.WriteLine("Updating the document");
   
   await driver.Execute(async txn =>
   {
       IQuery<Person> myQuery = txn.Query<Person>("UPDATE Person SET Age = ? WHERE FirstName = ?", 42, "John");
       await txn.Execute(myQuery);
   });
   ```

------
#### [ Sync ]

   ```
   Console.WriteLine("Updating the document");
   
   driver.Execute(txn =>
   {
       IQuery<Person> myQuery = txn.Query<Person>("UPDATE Person SET Age = ? WHERE FirstName = ?", 42, "John");
       txn.Execute(myQuery);
   });
   ```

------

1. Interroga nuovamente il documento per vedere il valore aggiornato.

------
#### [ Async ]

   ```
   Console.WriteLine("Querying the table for the updated document");
   
   IAsyncResult<Person> updateResult = await driver.Execute(async txn =>
   {
       IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
       return await txn.Execute(myQuery);
   });
   
   await foreach (Person person in updateResult)
   {
       Console.WriteLine(person);
       // John, Doe, 42
   }
   ```

------
#### [ Sync ]

   ```
   Console.WriteLine("Querying the table for the updated document");
   
   IResult<Person> updateResult = driver.Execute(txn =>
   {
       IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
       return txn.Execute(myQuery);
   });
   
   foreach (Person person in updateResult)
   {
       Console.WriteLine(person);
       // John, Doe, 42
   }
   ```

------

1. Per eseguire l'applicazione, immettete il seguente comando dalla directory principale della directory del `Amazon.QLDB.QuickStartGuide` progetto.

   ```
   $ dotnet run --project Amazon.QLDB.QuickStartGuide
   ```

### Utilizzo della libreria Ion


1. Aggiungi il codice seguente che aggiorna un documento nella `Person` tabella aggiornandolo `age` a 42.

------
#### [ Async ]

   ```
   Console.WriteLine("Updating the document");
   
   IIonValue ionIntAge = valueFactory.NewInt(42);
   IIonValue ionFirstName2 = valueFactory.NewString("John");
   
   await driver.Execute(async txn =>
   {
       await txn.Execute("UPDATE Person SET age = ? WHERE firstName = ?", ionIntAge, ionFirstName2);
   });
   ```

------
#### [ Sync ]

   ```
   Console.WriteLine("Updating a document");
   
   IIonValue ionIntAge = valueFactory.NewInt(42);
   IIonValue ionFirstName2 = valueFactory.NewString("John");
   
   driver.Execute(txn =>
   {
       txn.Execute("UPDATE Person SET age = ? WHERE firstName = ?", ionIntAge, ionFirstName2);
   });
   ```

------

1. Interroga nuovamente il documento per vedere il valore aggiornato.

------
#### [ Async ]

   ```
   Console.WriteLine("Querying the table for the updated document");
   
   IIonValue ionFirstName3 = valueFactory.NewString("John");
   
   IAsyncResult updateResult = await driver.Execute(async txn =>
   {
       return await txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName3 );
   });
   
   await foreach (IIonValue row in updateResult)
   {
       Console.WriteLine(row.GetField("firstName").StringValue);
       Console.WriteLine(row.GetField("lastName").StringValue);
       Console.WriteLine(row.GetField("age").IntValue);
   }
   ```

------
#### [ Sync ]

   ```
   Console.WriteLine("Querying the table for the updated document");
   
   IIonValue ionFirstName3 = valueFactory.NewString("John");
   
   IResult updateResult = driver.Execute(txn =>
   {
       return txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName3);
   });
   
   foreach (IIonValue row in updateResult)
   {
       Console.WriteLine(row.GetField("firstName").StringValue);
       Console.WriteLine(row.GetField("lastName").StringValue);
       Console.WriteLine(row.GetField("age").IntValue);
   }
   ```

------

1. Per eseguire l'applicazione, immettete il seguente comando dalla directory principale della directory del `Amazon.QLDB.QuickStartGuide` progetto.

   ```
   $ dotnet run --project Amazon.QLDB.QuickStartGuide
   ```

## Esecuzione dell'applicazione completa


Il seguente esempio di codice è la versione completa dell'`Program.cs`applicazione. Invece di eseguire i passaggi precedenti singolarmente, potete anche copiare ed eseguire questo esempio di codice dall'inizio alla fine. Questa applicazione dimostra alcune operazioni CRUD di base sul registro denominato. `quick-start`

**Nota**  
Prima di eseguire questo codice, assicuratevi di non avere già una tabella attiva denominata `Person` nel registro. `quick-start`

------
#### [ Async ]

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        public class Person
        {
            public string FirstName { get; set; }

            public string LastName { get; set; }

            public int Age { get; set; }

            public override string ToString()
            {
                return FirstName + ", " + LastName + ", " + Age.ToString();
            }
        }

        static async Task Main(string[] args)
        {
            Console.WriteLine("Create the async QLDB driver");
            IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
                .WithLedger("quick-start")
                .WithSerializer(new ObjectSerializer())
                .Build();

            Console.WriteLine("Creating the table and index");

            // Creates the table and the index in the same transaction.
            // Note: Any code within the lambda can potentially execute multiple times due to retries.
            // For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
            await driver.Execute(async txn =>
            {
                await txn.Execute("CREATE TABLE Person");
                await txn.Execute("CREATE INDEX ON Person(firstName)");
            });

            Console.WriteLine("Inserting a document");

            Person myPerson = new Person {
                FirstName = "John",
                LastName = "Doe",
                Age = 32
            };

            await driver.Execute(async txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("INSERT INTO Person ?", myPerson);
                await txn.Execute(myQuery);
            });

            Console.WriteLine("Querying the table");

            // The result from driver.Execute() is buffered into memory because once the
            // transaction is committed, streaming the result is no longer possible.
            IAsyncResult<Person> selectResult = await driver.Execute(async txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
                return await txn.Execute(myQuery);
            });

            await foreach (Person person in selectResult)
            {
                Console.WriteLine(person);
                // John, Doe, 32
            }

            Console.WriteLine("Updating the document");

            await driver.Execute(async txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("UPDATE Person SET Age = ? WHERE FirstName = ?", 42, "John");
                await txn.Execute(myQuery);
            });

            Console.WriteLine("Querying the table for the updated document");

            IAsyncResult<Person> updateResult = await driver.Execute(async txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
                return await txn.Execute(myQuery);
            });

            await foreach (Person person in updateResult)
            {
                Console.WriteLine(person);
                // John, Doe, 42
            }
        }
    }
}
```

------
#### [ Sync ]

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        public class Person
        {
            public string FirstName { get; set; }

            public string LastName { get; set; }

            public int Age { get; set; }

            public override string ToString()
            {
                return FirstName + ", " + LastName + ", " + Age.ToString();
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Create the sync QLDB driver");
            IQldbDriver driver = QldbDriver.Builder()
                .WithLedger("quick-start")
                .WithSerializer(new ObjectSerializer())
                .Build();

            Console.WriteLine("Creating the table and index");

            // Creates the table and the index in the same transaction.
            // Note: Any code within the lambda can potentially execute multiple times due to retries.
            // For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
            driver.Execute(txn =>
            {
                txn.Execute("CREATE TABLE Person");
                txn.Execute("CREATE INDEX ON Person(firstName)");
            });

            Console.WriteLine("Inserting a document");

            Person myPerson = new Person {
                FirstName = "John",
                LastName = "Doe",
                Age = 32
            };

            driver.Execute(txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("INSERT INTO Person ?", myPerson);
                txn.Execute(myQuery);
            });

            Console.WriteLine("Querying the table");

            // The result from driver.Execute() is buffered into memory because once the
            // transaction is committed, streaming the result is no longer possible.
            IResult<Person> selectResult = driver.Execute(txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
                return txn.Execute(myQuery);
            });

            foreach (Person person in selectResult)
            {
                Console.WriteLine(person);
                // John, Doe, 32
            }

            Console.WriteLine("Updating the document");

            driver.Execute(txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("UPDATE Person SET Age = ? WHERE FirstName = ?", 42, "John");
                txn.Execute(myQuery);
            });

            Console.WriteLine("Querying the table for the updated document");

            IResult<Person> updateResult = driver.Execute(txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
                return txn.Execute(myQuery);
            });

            foreach (Person person in updateResult)
            {
                Console.WriteLine(person);
                // John, Doe, 42
            }

        }
    }
}
```

------

### Utilizzo della libreria Ion


------
#### [ Async ]

```
using System;
using System.Threading.Tasks;
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        static IValueFactory valueFactory = new ValueFactory();

        static async Task Main(string[] args)
        {
            Console.WriteLine("Create the async QLDB driver");
            IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
                .WithLedger("quick-start")
                .Build();

            Console.WriteLine("Creating the table and index");

            // Creates the table and the index in the same transaction.
            // Note: Any code within the lambda can potentially execute multiple times due to retries.
            // For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
            await driver.Execute(async txn =>
            {
                await txn.Execute("CREATE TABLE Person");
                await txn.Execute("CREATE INDEX ON Person(firstName)");
            });

            Console.WriteLine("Inserting a document");

            // This is one way of creating Ion values. We can also use an IonLoader.
            // For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
            IIonValue ionPerson = valueFactory.NewEmptyStruct();
            ionPerson.SetField("firstName", valueFactory.NewString("John"));
            ionPerson.SetField("lastName", valueFactory.NewString("Doe"));
            ionPerson.SetField("age", valueFactory.NewInt(32));

            await driver.Execute(async txn =>
            {
                await txn.Execute("INSERT INTO Person ?", ionPerson);
            });

            Console.WriteLine("Querying the table");

            IIonValue ionFirstName = valueFactory.NewString("John");

            // The result from driver.Execute() is buffered into memory because once the
            // transaction is committed, streaming the result is no longer possible.
            IAsyncResult selectResult = await driver.Execute(async txn =>
            {
                return await txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName);
            });

            await foreach (IIonValue row in selectResult)
            {
                Console.WriteLine(row.GetField("firstName").StringValue);
                Console.WriteLine(row.GetField("lastName").StringValue);
                Console.WriteLine(row.GetField("age").IntValue);
            }

            Console.WriteLine("Updating the document");

            IIonValue ionIntAge = valueFactory.NewInt(42);
            IIonValue ionFirstName2 = valueFactory.NewString("John");

            await driver.Execute(async txn =>
            {
                await txn.Execute("UPDATE Person SET age = ? WHERE firstName = ?", ionIntAge, ionFirstName2);
            });

            Console.WriteLine("Querying the table for the updated document");

            IIonValue ionFirstName3 = valueFactory.NewString("John");

            IAsyncResult updateResult = await driver.Execute(async txn =>
            {
                return await txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName3);
            });

            await foreach (IIonValue row in updateResult)
            {
                Console.WriteLine(row.GetField("firstName").StringValue);
                Console.WriteLine(row.GetField("lastName").StringValue);
                Console.WriteLine(row.GetField("age").IntValue);
            }
        }
    }
}
```

------
#### [ Sync ]

```
using System;
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;
using Amazon.QLDB.Driver;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        static IValueFactory valueFactory = new ValueFactory();

        static void Main(string[] args)
        {
            Console.WriteLine("Create the sync QLDB Driver");
            IQldbDriver driver = QldbDriver.Builder()
                .WithLedger("quick-start")
                .Build();

            Console.WriteLine("Creating the tables and index");

            // Creates the table and the index in the same transaction.
            // Note: Any code within the lambda can potentially execute multiple times due to retries.
            // For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
            driver.Execute(txn =>
            {
                txn.Execute("CREATE TABLE Person");
                txn.Execute("CREATE INDEX ON Person(firstName)");
            });

            Console.WriteLine("Inserting a document");

            // This is one way of creating Ion values. We can also use an IonLoader.
            // For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
            IIonValue ionPerson = valueFactory.NewEmptyStruct();
            ionPerson.SetField("firstName", valueFactory.NewString("John"));
            ionPerson.SetField("lastName", valueFactory.NewString("Doe"));
            ionPerson.SetField("age", valueFactory.NewInt(32));

            driver.Execute(txn =>
            {
                txn.Execute("INSERT INTO Person ?", ionPerson);
            });

            Console.WriteLine("Querying the table");

            IIonValue ionFirstName = valueFactory.NewString("John");

            // The result from driver.Execute() is buffered into memory because once the
            // transaction is committed, streaming the result is no longer possible.
            IResult selectResult = driver.Execute(txn =>
            {
                return txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName);
            });

            foreach (IIonValue row in selectResult)
            {
                Console.WriteLine(row.GetField("firstName").StringValue);
                Console.WriteLine(row.GetField("lastName").StringValue);
                Console.WriteLine(row.GetField("age").IntValue);
            }

            Console.WriteLine("Updating a document");

            IIonValue ionIntAge = valueFactory.NewInt(42);
            IIonValue ionFirstName2 = valueFactory.NewString("John");

            driver.Execute(txn =>
            {
                txn.Execute("UPDATE Person SET age = ? WHERE firstName = ?", ionIntAge, ionFirstName2);
            });

            Console.WriteLine("Querying the table for the updated document");

            IIonValue ionFirstName3 = valueFactory.NewString("John");

            IResult updateResult = driver.Execute(txn =>
            {
                return txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName3);
            });

            foreach (IIonValue row in updateResult)
            {
                Console.WriteLine(row.GetField("firstName").StringValue);
                Console.WriteLine(row.GetField("lastName").StringValue);
                Console.WriteLine(row.GetField("age").IntValue);
            }
        }
    }
}
```

------

Per eseguire l'applicazione completa, immettete il seguente comando dalla directory principale della directory del `Amazon.QLDB.QuickStartGuide` progetto.

```
$ dotnet run --project Amazon.QLDB.QuickStartGuide
```

# Driver Amazon QLDB per.NET — Guida di riferimento al libro di cucina
Riferimento al libro di cucina

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questa guida di riferimento mostra i casi d'uso comuni del driver Amazon QLDB per.NET. Fornisce esempi di codice C\$1 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**  
Questo argomento fornisce esempi di codice per l'elaborazione di dati Amazon Ion utilizzando lo strumento di [mappatura di oggetti Ion per](https://github.com/amzn/ion-object-mapper-dotnet) impostazione predefinita. QLDB ha introdotto lo Ion object Mapper nella versione 1.3.0 del driver.NET. Ove applicabile, questo argomento fornisce anche esempi di codice che utilizzano la [libreria Ion](https://github.com/amzn/ion-dotnet) standard come alternativa. Per ulteriori informazioni, consulta [Lavorare con Amazon Ion](#cookbook-dotnet.ion).

**Contents**
+ [

## Importazione del driver
](#cookbook-dotnet.importing)
+ [

## Istanziazione del driver
](#cookbook-dotnet.instantiating)
+ [

## Operazioni CRUD
](#cookbook-dotnet.crud)
  + [

### Creazione di tabelle
](#cookbook-dotnet.crud.creating-tables)
  + [

### Creazione di indici
](#cookbook-dotnet.crud.creating-indexes)
  + [

### Leggere documenti
](#cookbook-dotnet.crud.reading)
    + [

#### Utilizzo dei parametri di interrogazione
](#cookbook-dotnet.reading-using-params)
  + [

### Inserimento di documenti
](#cookbook-dotnet.crud.inserting)
    + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-dotnet.crud.inserting.multiple)
  + [

### Aggiornamento dei documenti
](#cookbook-dotnet.crud.updating)
  + [

### Eliminazione di documenti
](#cookbook-dotnet.crud.deleting)
  + [

### Esecuzione di più istruzioni in una transazione
](#cookbook-dotnet.crud.multi-statement)
  + [

### Logica di ripetizione dei tentativi
](#cookbook-dotnet.crud.retry-logic)
  + [

### Implementazione di vincoli di unicità
](#cookbook-dotnet.crud.uniqueness-constraints)
+ [

## Lavorare con Amazon Ion
](#cookbook-dotnet.ion)
  + [

### Importazione del modulo Ion
](#cookbook-dotnet.ion.import)
  + [

### Creazione di tipi di ioni
](#cookbook-dotnet.ion.creating-types)
  + [

### Ottenere un dump binario Ion
](#cookbook-dotnet.ion.getting-binary)
  + [

### Ottenere un dump di testo Ion
](#cookbook-dotnet.ion.getting-text)

## Importazione del driver


Il seguente esempio di codice importa il driver.

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;
```

### Utilizzo della libreria Ion


```
using Amazon.QLDB.Driver;
using Amazon.IonDotnet.Builders;
```

## Istanziazione del driver


Il seguente esempio di codice crea un'istanza del driver che si connette a un nome di registro specificato utilizzando le impostazioni predefinite.

------
#### [ Async ]

```
IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
    .WithLedger("vehicle-registration")
    // Add Serialization library
    .WithSerializer(new ObjectSerializer())
    .Build();
```

------
#### [ Sync ]

```
IQldbDriver driver = QldbDriver.Builder()
    .WithLedger("vehicle-registration")
    // Add Serialization library
    .WithSerializer(new ObjectSerializer())
    .Build();
```

------

### Utilizzo della libreria Ion


------
#### [ Async ]

```
IAsyncQldbDriver driver = AsyncQldbDriver.Builder().WithLedger("vehicle-registration").Build();
```

------
#### [ Sync ]

```
IQldbDriver driver = QldbDriver.Builder().WithLedger("vehicle-registration").Build();
```

------

## Operazioni CRUD


QLDB *esegue operazioni di creazione, lettura, aggiornamento ed* eliminazione (CRUD) come parte di una transazione.

**avvertimento**  
Come best practice, 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 in 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 conflitti ottimistici di controllo della concorrenza (OCC).*

[Modello di concorrenza Amazon QLDB](concurrency.md)Per ulteriori informazioni su OCC, vedere.

**Transazioni create implicitamente**

Il driver [Amazon.QLDB.Driver. ](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.IQldbDriver.html)IQldb[Il metodo Driver.Execute accetta una funzione lambda che riceve un'istanza di Amazon.qldb.Driver. TransactionExecutor](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.TransactionExecutor.html), che è possibile utilizzare per eseguire le istruzioni. L'istanza di `TransactionExecutor` wraps una transazione creata implicitamente.

È possibile eseguire istruzioni all'interno della funzione lambda utilizzando il `Execute` metodo dell'esecutore della transazione. Il driver esegue implicitamente la transazione quando ritorna la funzione lambda.

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

**Contents**
+ [

### Creazione di tabelle
](#cookbook-dotnet.crud.creating-tables)
+ [

### Creazione di indici
](#cookbook-dotnet.crud.creating-indexes)
+ [

### Leggere documenti
](#cookbook-dotnet.crud.reading)
  + [

#### Utilizzo dei parametri di interrogazione
](#cookbook-dotnet.reading-using-params)
+ [

### Inserimento di documenti
](#cookbook-dotnet.crud.inserting)
  + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-dotnet.crud.inserting.multiple)
+ [

### Aggiornamento dei documenti
](#cookbook-dotnet.crud.updating)
+ [

### Eliminazione di documenti
](#cookbook-dotnet.crud.deleting)
+ [

### Esecuzione di più istruzioni in una transazione
](#cookbook-dotnet.crud.multi-statement)
+ [

### Logica di ripetizione dei tentativi
](#cookbook-dotnet.crud.retry-logic)
+ [

### Implementazione di vincoli di unicità
](#cookbook-dotnet.crud.uniqueness-constraints)

### Creazione di tabelle


------
#### [ Async ]

```
IAsyncResult<Table> createResult = await driver.Execute(async txn =>
{
    IQuery<Table> query = txn.Query<Table>("CREATE TABLE Person");
    return await txn.Execute(query);
});

await foreach (var result in createResult)
{
    Console.WriteLine("{ tableId: " + result.TableId + " }");
    // The statement returns the created table ID:
    // { tableId: 4o5Uk09OcjC6PpJpLahceE }
}
```

------
#### [ Sync ]

```
IResult<Table> createResult = driver.Execute( txn =>
{
    IQuery<Table> query = txn.Query<Table>("CREATE TABLE Person");
    return txn.Execute(query);
});

foreach (var result in createResult)
{
    Console.WriteLine("{ tableId: " + result.TableId + " }");
    // The statement returns the created table ID:
    // { tableId: 4o5Uk09OcjC6PpJpLahceE }
}
```

------

#### Utilizzo della libreria Ion


------
#### [ Async ]

```
// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("CREATE TABLE Person");
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the created table ID:
    // {
    //    tableId: "4o5Uk09OcjC6PpJpLahceE"
    // }
}
```

------
#### [ Sync ]

```
// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IResult result = driver.Execute(txn =>
{
    return txn.Execute("CREATE TABLE Person");
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the created table ID:
    // {
    //    tableId: "4o5Uk09OcjC6PpJpLahceE"
    // }
}
```

------

### Creazione di indici


------
#### [ Async ]

```
IAsyncResult<Table> createResult = await driver.Execute(async txn =>
{
    IQuery<Table> query = txn.Query<Table>("CREATE INDEX ON Person(firstName)");
    return await txn.Execute(query);
});

await foreach (var result in createResult)
{
    Console.WriteLine("{ tableId: " + result.TableId + " }");
    // The statement returns the updated table ID:
    // { tableId: 4o5Uk09OcjC6PpJpLahceE }
}
```

------
#### [ Sync ]

```
IResult<Table> createResult = driver.Execute(txn =>
{
    IQuery<Table> query = txn.Query<Table>("CREATE INDEX ON Person(firstName)");
    return txn.Execute(query);
});

foreach (var result in createResult)
{
    Console.WriteLine("{ tableId: " + result.TableId + " }");
    // The statement returns the updated table ID:
    // { tableId: 4o5Uk09OcjC6PpJpLahceE }
}
```

------

#### Utilizzo della libreria Ion


------
#### [ Async ]

```
IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("CREATE INDEX ON Person(GovId)");
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the updated table ID:
    // {
    //    tableId: "4o5Uk09OcjC6PpJpLahceE"
    // }
}
```

------
#### [ Sync ]

```
IResult result = driver.Execute(txn =>
{
    return txn.Execute("CREATE INDEX ON Person(GovId)");
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the updated table ID:
    // {
    //    tableId: "4o5Uk09OcjC6PpJpLahceE"
    // }
}
```

------

### Leggere documenti


```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }
// Person class is defined as follows:
// public class Person
// {
//     public string GovId { get; set; }
//     public string FirstName { get; set; }
//  }

IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'"));
});

await foreach (Person person in result)
{
    Console.WriteLine(person.GovId); // Prints TOYENC486FH.
    Console.WriteLine(person.FirstName); // Prints Brent.
}
```

**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](ql-reference.create-index.md) sul campo per ottimizzare le `GovId` prestazioni. Senza un indice attivo`GovId`, le query possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

#### Utilizzo dei parametri di interrogazione


Il seguente esempio di codice utilizza un parametro di query di tipo C\$1.

```
IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "Brent"));
});

await foreach (Person person in result)
{
    Console.WriteLine(person.GovId); // Prints TOYENC486FH.
    Console.WriteLine(person.FirstName); // Prints Brent.
}
```

Il seguente esempio di codice utilizza più parametri di query di tipo C\$1.

```
IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", "TOYENC486FH", "Brent"));
});

await foreach (Person person in result)
{
    Console.WriteLine(person.GovId); // Prints TOYENC486FH.
    Console.WriteLine(person.FirstName); // Prints Brent.
}
```

Il seguente esempio di codice utilizza una matrice di parametri di query di tipo C\$1.

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }
// { "GovId": "ROEE1C1AABH", "FirstName" : "Jim" }
// { "GovId": "YH844DA7LDB", "FirstName" : "Mary" }

string[] ids = {
    "TOYENC486FH",
    "ROEE1C1AABH",
    "YH844DA7LDB"
};

IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId IN (?,?,?)", ids));
});

await foreach (Person person in result)
{
    Console.WriteLine(person.FirstName); // Prints Brent on first iteration.
    // Prints Jim on second iteration.
    // Prints Mary on third iteration.
}
```

Il seguente esempio di codice utilizza un elenco C\$1 come valore.

```
// Assumes that Person table has document as follows:
// { "GovId": "TOYENC486FH",
//   "FirstName" : "Brent",
//   "Vehicles": [
//      { "Make": "Volkswagen",
//        "Model": "Golf"},
//      { "Make": "Honda",
//        "Model": "Civic"}
//   ]
// }
// Person class is defined as follows:
// public class Person
// {
//     public string GovId { get; set; }
//     public string FirstName { get; set; }
//     public List<Vehicle> Vehicles { get; set; }
// }
// Vehicle class is defined as follows:
// public class Vehicle
// {
//     public string Make { get; set; }
//     public string Model { get; set; }
// }

List<Vehicle> vehicles = new List<Vehicle>
{
    new Vehicle
    {
        Make = "Volkswagen",
        Model = "Golf"
    },
    new Vehicle
    {
        Make = "Honda",
        Model = "Civic"
    }
};

IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE Vehicles = ?", vehicles));
});

await foreach (Person person in result)
{
    Console.WriteLine("{");
    Console.WriteLine($"  GovId: {person.GovId},");
    Console.WriteLine($"  FirstName: {person.FirstName},");
    Console.WriteLine("  Vehicles: [");
    foreach (Vehicle vehicle in person.Vehicles)
    {
        Console.WriteLine("  {");
        Console.WriteLine($"    Make: {vehicle.Make},");
        Console.WriteLine($"    Model: {vehicle.Model},");
        Console.WriteLine("  },");
    }
    Console.WriteLine("  ]");
    Console.WriteLine("}");
    // Prints:
    // {
    //     GovId: TOYENC486FH,
    //     FirstName: Brent,
    //     Vehicles: [
    //     {
    //         Make: Volkswagen,
    //         Model: Golf
    //     },
    //     {
    //         Make: Honda,
    //         Model: Civic
    //     },
    //     ]
    // }
}
```

#### Utilizzo della libreria Ion


------
#### [ Async ]

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'");
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------
#### [ Sync ]

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'");
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------

**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](ql-reference.create-index.md) sul campo per ottimizzare le `GovId` prestazioni. Senza un indice attivo`GovId`, le query possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

Il seguente esempio di codice utilizza un parametro di query di tipo Ion.

------
#### [ Async ]

```
IValueFactory valueFactory = new ValueFactory();
IIonValue ionFirstName = valueFactory.NewString("Brent");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE FirstName = ?", ionFirstName);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------
#### [ Sync ]

```
IValueFactory valueFactory = new ValueFactory();
IIonValue ionFirstName = valueFactory.NewString("Brent");

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE FirstName = ?", ionFirstName);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------

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

------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");
IIonValue ionFirstName = valueFactory.NewString("Brent");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", ionGovId, ionFirstName);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");
IIonValue ionFirstName = valueFactory.NewString("Brent");

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", ionGovId, ionFirstName);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------

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

------
#### [ Async ]

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }
// { "GovId": "ROEE1C1AABH", "FirstName" : "Jim" }
// { "GovId": "YH844DA7LDB", "FirstName" : "Mary" }

IIonValue[] ionIds = {
    valueFactory.NewString("TOYENC486FH"),
    valueFactory.NewString("ROEE1C1AABH"),
    valueFactory.NewString("YH844DA7LDB")
};

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", ionIds);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent on first iteration.
                                                              // Prints Jim on second iteration.
                                                              // Prints Mary on third iteration.
}
```

------
#### [ Sync ]

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }
// { "GovId": "ROEE1C1AABH", "FirstName" : "Jim" }
// { "GovId": "YH844DA7LDB", "FirstName" : "Mary" }

IIonValue[] ionIds = {
    valueFactory.NewString("TOYENC486FH"),
    valueFactory.NewString("ROEE1C1AABH"),
    valueFactory.NewString("YH844DA7LDB")
};

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", ionIds);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent on first iteration.
                                                              // Prints Jim on second iteration.
                                                              // Prints Mary on third iteration.
}
```

------

Il seguente esempio di codice utilizza un elenco di ioni come valore. Per ulteriori informazioni sull'utilizzo di diversi tipi di ioni, vedere[Utilizzo dei tipi di dati Amazon Ion in Amazon QLDB](driver-working-with-ion.md).

------
#### [ Async ]

```
// Assumes that Person table has document as follows:
// { "GovId": "TOYENC486FH",
//   "FirstName" : "Brent",
//   "Vehicles": [
//      { "Make": "Volkswagen",
//        "Model": "Golf"},
//      { "Make": "Honda",
//        "Model": "Civic"}
//   ]
// }

IIonValue ionVehicle1 = valueFactory.NewEmptyStruct();
ionVehicle1.SetField("Make", valueFactory.NewString("Volkswagen"));
ionVehicle1.SetField("Model", valueFactory.NewString("Golf"));

IIonValue ionVehicle2 = valueFactory.NewEmptyStruct();
ionVehicle2.SetField("Make", valueFactory.NewString("Honda"));
ionVehicle2.SetField("Model", valueFactory.NewString("Civic"));

IIonValue ionVehicles =  valueFactory.NewEmptyList();
ionVehicles.Add(ionVehicle1);
ionVehicles.Add(ionVehicle2);

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE Vehicles = ?", ionVehicles);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // Prints:
    // {
    //     GovId: "TOYENC486FN",
    //     FirstName: "Brent",
    //     Vehicles: [
    //     {
    //         Make: "Volkswagen",
    //         Model: "Golf"
    //     },
    //     {
    //         Make: "Honda",
    //         Model: "Civic"
    //     }
    //     ]
    // }
}
```

------
#### [ Sync ]

```
// Assumes that Person table has document as follows:
// { "GovId": "TOYENC486FH",
//   "FirstName" : "Brent",
//   "Vehicles": [
//      { "Make": "Volkswagen",
//        "Model": "Golf"},
//      { "Make": "Honda",
//        "Model": "Civic"}
//   ]
// }

IIonValue ionVehicle1 = valueFactory.NewEmptyStruct();
ionVehicle1.SetField("Make", valueFactory.NewString("Volkswagen"));
ionVehicle1.SetField("Model", valueFactory.NewString("Golf"));

IIonValue ionVehicle2 = valueFactory.NewEmptyStruct();
ionVehicle2.SetField("Make", valueFactory.NewString("Honda"));
ionVehicle2.SetField("Model", valueFactory.NewString("Civic"));

IIonValue ionVehicles =  valueFactory.NewEmptyList();
ionVehicles.Add(ionVehicle1);
ionVehicles.Add(ionVehicle2);

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE Vehicles = ?", ionVehicles);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // Prints:
    // {
    //     GovId: "TOYENC486FN",
    //     FirstName: "Brent",
    //     Vehicles: [
    //     {
    //         Make: "Volkswagen",
    //         Model: "Golf"
    //     },
    //     {
    //         Make: "Honda",
    //         Model: "Civic"
    //     }
    //     ]
    // }
}
```

------

### Inserimento di documenti


Il seguente esempio di codice inserisce i tipi di dati Ion.

```
string govId = "TOYENC486FH";

Person person = new Person
{
    GovId = "TOYENC486FH",
    FirstName = "Brent"
};

await driver.Execute(async txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IAsyncResult<Person> result = await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = ?", govId));

    // Check if there is a record in the cursor.
    int count = await result.CountAsync();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    await txn.Execute(txn.Query<Document>("INSERT INTO Person ?", person));
});
```

#### Utilizzo della libreria Ion


------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH"));
ionPerson.SetField("FirstName", valueFactory.NewString("Brent"));

await driver.Execute(async txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IAsyncResult result = await txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId);

    // Check if there is a record in the cursor.
    int count = await result.CountAsync();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    await txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH"));
ionPerson.SetField("FirstName", valueFactory.NewString("Brent"));

driver.Execute(txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IResult result = txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId);

    // Check if there is a record in the cursor.
    int count = result.Count();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------

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 attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

#### Inserimento di più documenti in un'unica dichiarazione


Per inserire più documenti utilizzando un'unica [INSERT](ql-reference.insert.md) istruzione, è possibile passare un `List` parametro C\$1 all'istruzione nel modo seguente.

```
Person person1 = new Person
{
    FirstName = "Brent",
    GovId = "TOYENC486FH"
};

Person person2 = new Person
{
    FirstName = "Jim",
    GovId = "ROEE1C1AABH"
};

List<Person> people = new List<Person>();
people.Add(person1);
people.Add(person2);

IAsyncResult<Document> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Document>("INSERT INTO Person ?", people));
});

await foreach (Document row in result)
{
    Console.WriteLine("{ documentId: " + row.DocumentId + " }");
    // The statement returns the created documents' ID:
    // { documentId: 6BFt5eJQDFLBW2aR8LPw42 }
    // { documentId: K5Zrcb6N3gmIEHgGhwoyKF }
}
```

##### Utilizzo della libreria Ion


Per inserire più documenti utilizzando una singola [INSERT](ql-reference.insert.md) istruzione, è possibile passare un parametro di tipo [Ion list](driver-working-with-ion.md#driver-ion-list) all'istruzione come segue.

------
#### [ Async ]

```
IIonValue ionPerson1 = valueFactory.NewEmptyStruct();
ionPerson1.SetField("FirstName", valueFactory.NewString("Brent"));
ionPerson1.SetField("GovId", valueFactory.NewString("TOYENC486FH"));

IIonValue ionPerson2 = valueFactory.NewEmptyStruct();
ionPerson2.SetField("FirstName", valueFactory.NewString("Jim"));
ionPerson2.SetField("GovId", valueFactory.NewString("ROEE1C1AABH"));

IIonValue ionPeople = valueFactory.NewEmptyList();
ionPeople.Add(ionPerson1);
ionPeople.Add(ionPerson2);

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("INSERT INTO Person ?", ionPeople);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the created documents' ID:
    // {
    //     documentId: "6BFt5eJQDFLBW2aR8LPw42"
    // }
    //
    // {
    //     documentId: "K5Zrcb6N3gmIEHgGhwoyKF"
    // }
}
```

------
#### [ Sync ]

```
IIonValue ionPerson1 = valueFactory.NewEmptyStruct();
ionPerson1.SetField("FirstName", valueFactory.NewString("Brent"));
ionPerson1.SetField("GovId", valueFactory.NewString("TOYENC486FH"));

IIonValue ionPerson2 = valueFactory.NewEmptyStruct();
ionPerson2.SetField("FirstName", valueFactory.NewString("Jim"));
ionPerson2.SetField("GovId", valueFactory.NewString("ROEE1C1AABH"));

IIonValue ionPeople = valueFactory.NewEmptyList();
ionPeople.Add(ionPerson1);
ionPeople.Add(ionPerson2);

IResult result = driver.Execute(txn =>
{
    return txn.Execute("INSERT INTO Person ?", ionPeople);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the created documents' ID:
    // {
    //     documentId: "6BFt5eJQDFLBW2aR8LPw42"
    // }
    //
    // {
    //     documentId: "K5Zrcb6N3gmIEHgGhwoyKF"
    // }
}
```

------

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

### Aggiornamento dei documenti


```
string govId = "TOYENC486FH";
string firstName = "John";

IAsyncResult<Document> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Document>("UPDATE Person SET FirstName = ? WHERE GovId = ?", firstName , govId));
});

await foreach (Document row in result)
{
    Console.WriteLine("{ documentId: " + row.DocumentId + " }");
    // The statement returns the updated document ID:
    // { documentId: Djg30Zoltqy5M4BFsA2jSJ }
}
```

#### Utilizzo della libreria Ion


------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");
IIonValue ionFirstName = valueFactory.NewString("John");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", ionFirstName , ionGovId);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the updated document ID:
    // {
    //     documentId: "Djg30Zoltqy5M4BFsA2jSJ"
    // }
}
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");
IIonValue ionFirstName = valueFactory.NewString("John");

IResult result = driver.Execute(txn =>
{
    return txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", ionFirstName , ionGovId);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the updated document ID:
    // {
    //     documentId: "Djg30Zoltqy5M4BFsA2jSJ"
    // }
}
```

------

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

### Eliminazione di documenti


```
string govId = "TOYENC486FH";

IAsyncResult<Document> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Document>("DELETE FROM Person WHERE GovId = ?", govId));
});

await foreach (Document row in result)
{
    Console.WriteLine("{ documentId: " + row.DocumentId + " }");
    // The statement returns the updated document ID:
    // { documentId: Djg30Zoltqy5M4BFsA2jSJ }
}
```

#### Utilizzo della libreria Ion


------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("DELETE FROM Person WHERE GovId = ?", ionGovId);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the deleted document ID:
    // {
    //     documentId: "Djg30Zoltqy5M4BFsA2jSJ"
    // }
}
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IResult result = driver.Execute(txn =>
{
    return txn.Execute("DELETE FROM Person WHERE GovId = ?", ionGovId);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the deleted document ID:
    // {
    //     documentId: "Djg30Zoltqy5M4BFsA2jSJ"
    // }
}
```

------

**Nota**  
In questo esempio, consigliamo di avere un indice sul `GovId` campo per ottimizzare le prestazioni. Senza un indice attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC 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 async Task<bool> InsureVehicle(IAsyncQldbDriver driver, string vin)
{
    return await driver.Execute(async txn =>
    {
        // Check if the vehicle is insured.
        Amazon.QLDB.Driver.Generic.IAsyncResult<Vehicle> result = await txn.Execute(
            txn.Query<Vehicle>("SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin));

        if (await result.CountAsync() > 0)
        {
            // If the vehicle is not insured, insure it.
            await txn.Execute(
                txn.Query<Document>("UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin));
            return true;
        }
        return false;
    });
}
```

#### Utilizzo della libreria Ion


------
#### [ Async ]

```
// 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 async Task<bool> InsureVehicle(IAsyncQldbDriver driver, string vin)
{
    ValueFactory valueFactory = new ValueFactory();
    IIonValue ionVin = valueFactory.NewString(vin);

    return await driver.Execute(async txn =>
    {
        // Check if the vehicle is insured.
        Amazon.QLDB.Driver.IAsyncResult result = await txn.Execute(
            "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", ionVin);

        if (await result.CountAsync() > 0)
        {
            // If the vehicle is not insured, insure it.
            await txn.Execute(
                "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", ionVin);
            return true;
        }
        return false;
    });
}
```

------

### Logica di ripetizione dei tentativi


Per informazioni sulla logica di ripetizione integrata nel driver, vedere[Comprendere la politica dei nuovi tentativi con il driver in Amazon QLDB](driver-retry-policy.md).

### Implementazione di vincoli di unicità


QLDB non 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 specificato`GovId`.

1. 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'eccezione relativa al conflitto OCC.

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

```
string govId = "TOYENC486FH";

Person person = new Person
{
    GovId = "TOYENC486FH",
    FirstName = "Brent"
};

await driver.Execute(async txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IAsyncResult<Person> result = await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = ?", govId));

    // Check if there is a record in the cursor.
    int count = await result.CountAsync();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    await txn.Execute(txn.Query<Document>("INSERT INTO Person ?", person));
});
```

#### Utilizzo della libreria Ion


------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH"));
ionPerson.SetField("FirstName", valueFactory.NewString("Brent"));

await driver.Execute(async txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IAsyncResult result = await txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId);

    // Check if there is a record in the cursor.
    int count = await result.CountAsync();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    await txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH"));
ionPerson.SetField("FirstName", valueFactory.NewString("Brent"));

driver.Execute(txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IResult result = txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId);

    // Check if there is a record in the cursor.
    int count = result.Count();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------

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

## Lavorare con Amazon Ion


Esistono diversi modi per elaborare i dati di Amazon Ion in QLDB. Puoi usare la [libreria Ion](https://github.com/amzn/ion-dotnet) per creare e modificare valori Ion. In alternativa, puoi utilizzare lo [Ion object Mapper per mappare](https://github.com/amzn/ion-object-mapper-dotnet) i *semplici vecchi oggetti CLR* (POCO) di C\$1 da e verso i valori Ion. La versione 1.3.0 del driver QLDB per.NET introduce il supporto per il mappatore di oggetti Ion.

Le sezioni seguenti forniscono esempi di codice per l'elaborazione di dati Ion utilizzando entrambe le tecniche.

**Contents**
+ [

### Importazione del modulo Ion
](#cookbook-dotnet.ion.import)
+ [

### Creazione di tipi di ioni
](#cookbook-dotnet.ion.creating-types)
+ [

### Ottenere un dump binario Ion
](#cookbook-dotnet.ion.getting-binary)
+ [

### Ottenere un dump di testo Ion
](#cookbook-dotnet.ion.getting-text)

### Importazione del modulo Ion


```
using Amazon.IonObjectMapper;
```

#### Utilizzo della libreria Ion


```
using Amazon.IonDotnet.Builders;
```

### Creazione di tipi di ioni


Il seguente esempio di codice mostra come creare valori Ion da oggetti C\$1 utilizzando il mappatore di oggetti Ion.

```
// Assumes that Person class is defined as follows:
// public class Person
// {
//     public string FirstName { get; set; }
//     public int Age { get; set; }
// }

// Initialize the Ion Object Mapper
IonSerializer ionSerializer = new IonSerializer();

// The C# object to be serialized
Person person = new Person
{
    FirstName = "John",
    Age = 13
};

// Serialize the C# object into stream using the Ion Object Mapper
Stream stream = ionSerializer.Serialize(person);

// Load will take in stream and return a datagram; a top level container of Ion values.
IIonValue ionDatagram = IonLoader.Default.Load(stream);

// To get the Ion value within the datagram, we call GetElementAt(0).
IIonValue ionPerson = ionDatagram.GetElementAt(0);

Console.WriteLine(ionPerson.GetField("firstName").StringValue);
Console.WriteLine(ionPerson.GetField("age").IntValue);
```

#### Utilizzo della libreria Ion


I seguenti esempi di codice mostrano i due modi per creare valori Ion utilizzando la libreria Ion.

**Uso di `ValueFactory`**

```
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;

IValueFactory valueFactory = new ValueFactory();

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("firstName", valueFactory.NewString("John"));
ionPerson.SetField("age", valueFactory.NewInt(13));

Console.WriteLine(ionPerson.GetField("firstName").StringValue);
Console.WriteLine(ionPerson.GetField("age").IntValue);
```

**Uso di `IonLoader`**

```
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;

// Load will take in Ion text and return a datagram; a top level container of Ion values.
IIonValue ionDatagram = IonLoader.Default.Load("{firstName: \"John\", age: 13}");

// To get the Ion value within the datagram, we call GetElementAt(0).
IIonValue ionPerson = ionDatagram.GetElementAt(0);

Console.WriteLine(ionPerson.GetField("firstName").StringValue);
Console.WriteLine(ionPerson.GetField("age").IntValue);
```

### Ottenere un dump binario Ion


```
// Initialize the Ion Object Mapper with Ion binary serialization format
IonSerializer ionSerializer = new IonSerializer(new IonSerializationOptions
{
    Format = IonSerializationFormat.BINARY
});

// The C# object to be serialized
Person person = new Person
{
    FirstName = "John",
    Age = 13
};

MemoryStream stream = (MemoryStream) ionSerializer.Serialize(person);
Console.WriteLine(BitConverter.ToString(stream.ToArray()));
```

#### Utilizzo della libreria Ion


```
// ionObject is an Ion struct
MemoryStream stream = new MemoryStream();
using (var writer = IonBinaryWriterBuilder.Build(stream))
{
    ionObject.WriteTo(writer);
    writer.Finish();
}

Console.WriteLine(BitConverter.ToString(stream.ToArray()));
```

### Ottenere un dump di testo Ion


```
// Initialize the Ion Object Mapper
IonSerializer ionSerializer = new IonSerializer(new IonSerializationOptions
{
    Format = IonSerializationFormat.TEXT
});

// The C# object to be serialized
Person person = new Person
{
    FirstName = "John",
    Age = 13
};

MemoryStream stream = (MemoryStream) ionSerializer.Serialize(person);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(stream.ToArray()));
```

#### Utilizzo della libreria Ion


```
// ionObject is an Ion struct
StringWriter sw = new StringWriter();
using (var writer = IonTextWriterBuilder.Build(sw))
{
    ionObject.WriteTo(writer);
    writer.Finish();
}

Console.WriteLine(sw.ToString());
```

Per ulteriori informazioni sull'utilizzo di Ion, consulta la [documentazione di Amazon Ion](http://amzn.github.io/ion-docs/) su GitHub. Per altri esempi di codice sull'utilizzo di Ion in QLDB, vedere. [Utilizzo dei tipi di dati Amazon Ion in Amazon QLDB](driver-working-with-ion.md)

# Driver Amazon QLDB per Go
Vai all'autista

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Per utilizzare i dati nel registro, puoi connetterti ad Amazon QLDB dall'applicazione Go utilizzando un driver fornito. AWS I seguenti argomenti descrivono come iniziare a usare il driver QLDB per Go.

**Topics**
+ [

## Risorse per i driver
](#getting-started.golang.resources)
+ [

## Prerequisiti
](#getting-started.golang.prereqs)
+ [

## Installazione
](#getting-started.golang.install)
+ [Tutorial di avvio rapido](driver-quickstart-golang.md)
+ [Riferimento al libro di cucina](driver-cookbook-golang.md)

## Risorse per i driver


Per ulteriori informazioni sulle funzionalità supportate dal driver Go, consulta le seguenti risorse:
+ [Riferimento API: [3.x, 2.x](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver)[, 1.x](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver)](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/qldbdriver)
+ [Codice sorgente del driver () GitHub](https://github.com/awslabs/amazon-qldb-driver-go)
+ [Ricettario Amazon Ion](http://amzn.github.io/ion-docs/guides/cookbook.html)

## Prerequisiti


Prima di iniziare a usare il driver QLDB per Go, devi fare quanto segue:

1. Segui le istruzioni di AWS configurazione riportate in. [Accesso ad Amazon QLDB](accessing.md) Questo include gli output seguenti:

   1. Iscriviti a AWS.

   1. Crea un utente con le autorizzazioni QLDB appropriate.

   1. Concedi l'accesso programmatico per lo sviluppo.

1. (Facoltativo) Installa un ambiente di sviluppo integrato (IDE) a tua scelta. Per un elenco di quelli comunemente usati IDEs per Go, consulta [Editor plugins e IDEs](https://golang.org/doc/editors.html) sul sito web di Go.

1. Scarica e installa una delle seguenti versioni di Go dal sito di [download di Go](https://golang.org/dl/):
   + **1.15 o versione successiva — driver** QLDB per Go v3
   + **1.14** — Driver QLDB per Go v1 o v2

1. Configura il tuo ambiente di sviluppo per: [AWS SDK per Go](https://aws.amazon.com/sdk-for-go)

   1. Configura le tue AWS credenziali. Ti consigliamo di creare un file di credenziali condiviso.

      *Per istruzioni, consulta [Specificare le credenziali nella Guida per](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-credentials) gli AWS SDK per Go sviluppatori.*

   1. Imposta i tuoi valori predefiniti. Regione AWS Per informazioni su come, vedi [Specificare il Regione AWS](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-the-aws-region).

      Per un elenco completo delle regioni disponibili, consulta gli [endpoint e le quote di Amazon QLDB](https://docs.aws.amazon.com/general/latest/gr/qldb.html) nel. *Riferimenti generali di AWS*

Successivamente, puoi configurare un'applicazione di esempio di base ed eseguire esempi di codice breve, oppure puoi installare il driver in un progetto Go esistente.
+ Per installare il driver QLDB e AWS SDK per Go poi in un progetto esistente, procedi con. [Installazione](#getting-started.golang.install)
+ Per configurare un progetto ed eseguire brevi esempi di codice che illustrano le transazioni di dati di base su un registro, consulta il. [Tutorial di avvio rapido](driver-quickstart-golang.md)

## Installazione


[Il driver QLDB per Go è open source nel GitHub repository awslabs/. amazon-qldb-driver-go](http://github.com/awslabs/amazon-qldb-driver-go) QLDB supporta le seguenti versioni dei driver e le relative dipendenze Go.


****  

| Versione driver | Versione Go | Stato | Data di uscita più recente | 
| --- | --- | --- | --- | 
| [1.x](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/qldbdriver) | 1.14 o successivo | Versione di produzione | 16 giugno 2021 | 
| [2.x](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver) | 1.14 o successivo | Versione di produzione | 21 luglio 2021 | 
| [3.x](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver) | 1.15 o versione successiva | Versione di produzione | 10 novembre 2022 | 

**Per installare il driver**

1. Assicurati che il tuo progetto utilizzi i [moduli Go](https://blog.golang.org/using-go-modules) per installare le dipendenze del progetto.

1. Nella directory del progetto, inserisci il seguente `go get` comando.

------
#### [ 3.x ]

   ```
   $ go get -u github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver
   ```

------
#### [ 2.x ]

   ```
   $ go get -u github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver
   ```

------

L'installazione del driver installa anche le sue dipendenze, inclusi i pacchetti [AWS SDK per Go](https://github.com/aws/aws-sdk-go)or [AWS SDK per Go v2](https://github.com/aws/aws-sdk-go-v2) e [Amazon](https://github.com/amzn/ion-go) Ion.

Per brevi esempi di codice su come eseguire transazioni di dati di base su un registro, consulta il. [Riferimento al libro di cucina](driver-cookbook-golang.md)

# Driver Amazon QLDB per Go — Tutorial di avvio rapido
Tutorial di avvio rapido

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo tutorial, imparerai come configurare una semplice applicazione utilizzando l'ultima versione del driver Amazon QLDB per Go. Questa guida include i passaggi per l'installazione del driver e brevi esempi in codice delle operazioni di base di *creazione, lettura, aggiornamento ed eliminazione* (CRUD).

**Topics**
+ [

## Prerequisiti
](#driver-quickstart-golang.prereqs)
+ [

## Passaggio 1: installare il driver
](#driver-quickstart-golang.install)
+ [

## Fase 2: Importazione dei pacchetti
](#driver-quickstart-golang.import)
+ [

## Fase 3: Inizializzare il driver
](#driver-quickstart-golang.initialize)
+ [

## Fase 4: Creare una tabella e un indice
](#driver-quickstart-golang.create-table-index)
+ [

## Fase 5: Inserimento di un documento
](#driver-quickstart-golang.insert)
+ [

## Fase 6: Interrogare il documento
](#driver-quickstart-golang.query)
+ [

## Fase 7: Aggiornare il documento
](#driver-quickstart-golang.update)
+ [

## Fase 8: Interrogare il documento aggiornato
](#driver-quickstart-golang.query-2)
+ [

## Fase 9: Eliminare la tabella
](#driver-quickstart-golang.drop-table)
+ [

## Esecuzione dell'applicazione completa
](#driver-quickstart-golang.complete)

## Prerequisiti


Prima di iniziare, assicuratevi di fare quanto segue:

1. Completa il driver [Prerequisiti](getting-started.golang.md#getting-started.golang.prereqs) for the Go, se non l'hai già fatto. Ciò include la registrazione AWS, la concessione dell'accesso programmatico per lo sviluppo e l'installazione di Go.

1. Crea un libro mastro denominato. `quick-start`

   Per informazioni su come creare un registro, consulta [Operazioni di base per i registri Amazon QLDB](ledger-management.basics.md) o [Fase 1: Creare un nuovo libro contabile](getting-started-step-1.md) in *Guida introduttiva alla console*.

## Passaggio 1: installare il driver


Assicurati che il tuo progetto utilizzi i [moduli Go](https://blog.golang.org/using-go-modules) per installare le dipendenze del progetto.

Nella directory del progetto, inserisci il seguente `go get` comando.

```
$ go get -u github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver
```

L'installazione del driver installa anche le sue dipendenze, inclusi i pacchetti [AWS SDK per Go v2 e](https://github.com/aws/aws-sdk-go-v2) [Amazon](https://github.com/amzn/ion-go) Ion.

## Fase 2: Importazione dei pacchetti


Importa i seguenti AWS pacchetti.

```
import (
    "context"
    "fmt"

    "github.com/amzn/ion-go/ion"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/qldbSession"
    "github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver"
)
```

## Fase 3: Inizializzare il driver


Inizializza un'istanza del driver che si connette al registro denominato. `quick-start`

```
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
  panic(err)
}

qldbSession := qldbsession.NewFromConfig(cfg, func(options *qldbsession.Options) {
    options.Region = "us-east-1"
})
driver, err := qldbdriver.New(
    "quick-start",
    qldbSession,
    func(options *qldbdriver.DriverOptions) {
        options.LoggerVerbosity = qldbdriver.LogInfo
    })
if err != nil {
    panic(err)
}

defer driver.Shutdown(context.Background())
```

**Nota**  
In questo esempio di codice, sostituiscilo *us-east-1* con il Regione AWS punto in cui hai creato il registro.

## Fase 4: Creare una tabella e un indice


Il seguente esempio di codice mostra come eseguire le `CREATE INDEX` istruzioni `CREATE TABLE` e le istruzioni.

```
_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    _, err := txn.Execute("CREATE TABLE People")
    if err != nil {
        return nil, err
    }

    // When working with QLDB, it's recommended to create an index on fields we're filtering on.
    // This reduces the chance of OCC conflict exceptions with large datasets.
    _, err = txn.Execute("CREATE INDEX ON People (firstName)")
    if err != nil {
        return nil, err
    }

    _, err = txn.Execute("CREATE INDEX ON People (age)")
    if err != nil {
        return nil, err
    }

    return nil, nil
})
if err != nil {
    panic(err)
}
```

Questo codice crea una tabella `People` denominata e gli indici per i `age` campi `firstName` e in quella tabella. [Gli indici](ql-reference.create-index.md) sono necessari per ottimizzare le prestazioni delle query e aiutano a limitare le eccezioni ai conflitti OCC ([Optimistic Concurrency Control](concurrency.md)).

## Fase 5: Inserimento di un documento


I seguenti esempi di codice mostrano come eseguire un'`INSERT`istruzione. QLDB supporta il linguaggio di interrogazione [PartiQL](ql-reference.md) (compatibile con SQL) e il formato dati [Amazon](ion.md) Ion (superset di JSON).

### Usare PartiQl letterale


Il codice seguente inserisce un documento nella `People` tabella utilizzando un'istruzione PartiQL letterale a stringa.

```
_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    return txn.Execute("INSERT INTO People {'firstName': 'Jane', 'lastName': 'Doe', 'age': 77}")
})
if err != nil {
    panic(err)
}
```

### Utilizzo dei tipi di dati Ion


Analogamente al [pacchetto JSON](https://golang.org/pkg/encoding/json/) integrato di Go, puoi eseguire il marshal e il unmarshal dei tipi di dati Go da e verso Ion.

1. Supponiamo di avere la seguente struttura Go denominata. `Person`

   ```
   type Person struct {
       FirstName string `ion:"firstName"`
       LastName  string `ion:"lastName"`
       Age       int    `ion:"age"`
   }
   ```

1. Creare un'istanza di `Person`.

   ```
   person := Person{"John", "Doe", 54}
   ```

   Il driver mostra una rappresentazione testuale con codifica ION di for you. `person`
**Importante**  
Affinché marshal e unmarshal funzionino correttamente, i nomi dei campi della struttura dati Go devono essere esportati (con la prima lettera maiuscola).

1. Passa l'istanza al metodo della transazione. `person` `Execute`

   ```
   _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
       return txn.Execute("INSERT INTO People ?", person)
   })
   if err != nil {
       panic(err)
   }
   ```

   Questo esempio utilizza un punto interrogativo (`?`) come segnaposto variabile per passare le informazioni del documento all'istruzione. Quando si utilizzano i segnaposto, è necessario passare un valore di testo con codifica ION.
**Suggerimento**  
Per inserire più documenti utilizzando una sola [INSERT](ql-reference.insert.md) istruzione, è possibile passare un parametro di tipo [elenco](driver-working-with-ion.md#driver-ion-list) all'istruzione nel modo seguente.  

   ```
   // people is a list
   txn.Execute("INSERT INTO People ?", people)
   ```
Non racchiudete la variabile placeholder (`?`) tra parentesi angolari doppie (`<<...>>`) quando passate un elenco. *Nelle istruzioni PartiQL manuali, le parentesi doppie angolari indicano una raccolta non ordinata nota come borsa.*

## Fase 6: Interrogare il documento


Il seguente esempio di codice mostra come eseguire un'`SELECT`istruzione.

```
p, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    result, err := txn.Execute("SELECT firstName, lastName, age FROM People WHERE age = 54")
    if err != nil {
        return nil, err
    }

    // Assume the result is not empty
    hasNext := result.Next(txn)
    if !hasNext && result.Err() != nil {
        return nil, result.Err()
    }

    ionBinary := result.GetCurrentData()

    temp := new(Person)
    err = ion.Unmarshal(ionBinary, temp)
    if err != nil {
        return nil, err
    }

    return *temp, nil
})
if err != nil {
    panic(err)
}

var returnedPerson Person
returnedPerson = p.(Person)

if returnedPerson != person {
    fmt.Print("Queried result does not match inserted struct")
}
```

Questo esempio esegue una query sul documento dalla `People` tabella, presuppone che il set di risultati non sia vuoto e restituisce il documento in base al risultato.

## Fase 7: Aggiornare il documento


Il seguente esempio di codice mostra come eseguire un'`UPDATE`istruzione.

```
person.Age += 10

_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    return txn.Execute("UPDATE People SET age = ? WHERE firstName = ?", person.Age, person.FirstName)
})
if err != nil {
    panic(err)
}
```

## Fase 8: Interrogare il documento aggiornato


Il seguente esempio di codice esegue una query sulla `People` tabella `firstName` e restituisce tutti i documenti del set di risultati.

```
p, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    result, err := txn.Execute("SELECT firstName, lastName, age FROM People WHERE firstName = ?", person.FirstName)
    if err != nil {
        return nil, err
    }

    var people []Person
    for result.Next(txn) {
        ionBinary := result.GetCurrentData()

        temp := new(Person)
        err = ion.Unmarshal(ionBinary, temp)
        if err != nil {
            return nil, err
        }

        people = append(people, *temp)
    }
    if result.Err() != nil {
        return nil, result.Err()
    }

    return people, nil
})
if err != nil {
    panic(err)
}

var people []Person
people = p.([]Person)

updatedPerson := Person{"John", "Doe", 64}
if people[0] != updatedPerson {
    fmt.Print("Queried result does not match updated struct")
}
```

## Fase 9: Eliminare la tabella


Il seguente esempio di codice mostra come eseguire un'`DROP TABLE`istruzione.

```
_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    return txn.Execute("DROP TABLE People")
})
if err != nil {
    panic(err)
}
```

## Esecuzione dell'applicazione completa


Il seguente esempio di codice è la versione completa dell'applicazione. Invece di eseguire i passaggi precedenti singolarmente, potete anche copiare ed eseguire questo esempio di codice dall'inizio alla fine. Questa applicazione dimostra alcune operazioni CRUD di base sul registro denominato. `quick-start`

**Nota**  
Prima di eseguire questo codice, assicuratevi di non avere già una tabella attiva denominata `People` nel registro. `quick-start`

```
package main

import (
    "context"
    "fmt"

    "github.com/amzn/ion-go/ion"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/qldbsession"
    "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)

func main() {
    awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1")))
    qldbSession := qldbsession.New(awsSession)

    driver, err := qldbdriver.New(
        "quick-start",
        qldbSession,
        func(options *qldbdriver.DriverOptions) {
            options.LoggerVerbosity = qldbdriver.LogInfo
        })
    if err != nil {
        panic(err)
    }
    defer driver.Shutdown(context.Background())

    _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
        _, err := txn.Execute("CREATE TABLE People")
        if err != nil {
            return nil, err
        }

        // When working with QLDB, it's recommended to create an index on fields we're filtering on.
        // This reduces the chance of OCC conflict exceptions with large datasets.
        _, err = txn.Execute("CREATE INDEX ON People (firstName)")
        if err != nil {
            return nil, err
        }

        _, err = txn.Execute("CREATE INDEX ON People (age)")
        if err != nil {
            return nil, err
        }

        return nil, nil
    })
    if err != nil {
        panic(err)
    }

    _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
        return txn.Execute("INSERT INTO People {'firstName': 'Jane', 'lastName': 'Doe', 'age': 77}")
    })
    if err != nil {
        panic(err)
    }

    type Person struct {
        FirstName string `ion:"firstName"`
        LastName  string `ion:"lastName"`
        Age       int    `ion:"age"`
    }

    person := Person{"John", "Doe", 54}

    _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
        return txn.Execute("INSERT INTO People ?", person)
    })
    if err != nil {
        panic(err)
    }

    p, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
        result, err := txn.Execute("SELECT firstName, lastName, age FROM People WHERE age = 54")
        if err != nil {
            return nil, err
        }

        // Assume the result is not empty
        hasNext := result.Next(txn)
        if !hasNext && result.Err() != nil {
            return nil, result.Err()
        }

        ionBinary := result.GetCurrentData()

        temp := new(Person)
        err = ion.Unmarshal(ionBinary, temp)
        if err != nil {
            return nil, err
        }

        return *temp, nil
    })
    if err != nil {
        panic(err)
    }

    var returnedPerson Person
    returnedPerson = p.(Person)

    if returnedPerson != person {
        fmt.Print("Queried result does not match inserted struct")
    }

    person.Age += 10

    _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
        return txn.Execute("UPDATE People SET age = ? WHERE firstName = ?", person.Age, person.FirstName)
    })
    if err != nil {
        panic(err)
    }

    p, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
        result, err := txn.Execute("SELECT firstName, lastName, age FROM People WHERE firstName = ?", person.FirstName)
        if err != nil {
            return nil, err
        }

        var people []Person
        for result.Next(txn) {
            ionBinary := result.GetCurrentData()

            temp := new(Person)
            err = ion.Unmarshal(ionBinary, temp)
            if err != nil {
                return nil, err
            }

            people = append(people, *temp)
        }
        if result.Err() != nil {
            return nil, result.Err()
        }

        return people, nil
    })
    if err != nil {
        panic(err)
    }

    var people []Person
    people = p.([]Person)

    updatedPerson := Person{"John", "Doe", 64}
    if people[0] != updatedPerson {
        fmt.Print("Queried result does not match updated struct")
    }

    _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
        return txn.Execute("DROP TABLE People")
    })
    if err != nil {
        panic(err)
    }
}
```

# Driver Amazon QLDB per Go: riferimento al libro di cucina
Riferimento al libro di cucina

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questa guida di riferimento mostra i casi d'uso comuni del driver Amazon QLDB per Go. Fornisce esempi di codice Go 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 QLDB per Go.

**Contents**
+ [

## Importazione del driver
](#cookbook-golang.importing)
+ [

## Istanziazione del driver
](#cookbook-golang.instantiating)
+ [

## Operazioni CRUD
](#cookbook-golang.crud)
  + [

### Creazione di tabelle
](#cookbook-golang.crud.creating-tables)
  + [

### Creazione di indici
](#cookbook-golang.crud.creating-indexes)
  + [

### Leggere documenti
](#cookbook-golang.crud.reading)
    + [

#### Utilizzo dei parametri di interrogazione
](#cookbook-golang.reading-using-params)
  + [

### Inserimento di documenti
](#cookbook-golang.crud.inserting)
    + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-golang.crud.inserting.multiple)
  + [

### Aggiornamento dei documenti
](#cookbook-golang.crud.updating)
  + [

### Eliminazione di documenti
](#cookbook-golang.crud.deleting)
  + [

### Esecuzione di più istruzioni in una transazione
](#cookbook-golang.crud.multi-statement)
  + [

### Logica di ripetizione dei tentativi
](#cookbook-golang.crud.retry-logic)
  + [

### Implementazione di vincoli di unicità
](#cookbook-golang.crud.uniqueness-constraints)
+ [

## Lavorare con Amazon Ion
](#cookbook-golang.ion)
  + [

### Importazione del modulo Ion
](#cookbook-golang.ion.import)
  + [

### Creazione di tipi di ioni
](#cookbook-golang.ion.creating-types)
  + [

### Ottenere il binario Ion
](#cookbook-golang.ion.getting-binary)
  + [

### Ricevere testo Ion
](#cookbook-golang.ion.getting-text)

## Importazione del driver


Il seguente esempio di codice importa il driver e gli altri AWS pacchetti richiesti.

------
#### [ 3.x ]

```
import (

    "github.com/amzn/ion-go/ion"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/qldbSession"
    "github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver"
)
```

------
#### [ 2.x ]

```
import (

    "github.com/amzn/ion-go/ion"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/qldbsession"
    "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)
```

------

**Nota**  
Questo esempio importa anche il pacchetto Amazon Ion (`amzn/ion-go/ion`). Questo pacchetto è necessario per elaborare i dati Ion durante l'esecuzione di alcune operazioni sui dati in questo riferimento. Per ulteriori informazioni, consulta [Lavorare con Amazon Ion](#cookbook-golang.ion).

## Istanziazione del driver


Il seguente esempio di codice crea un'istanza del driver che si connette a un nome di registro specificato in un determinato periodo. Regione AWS

------
#### [ 3.x ]

```
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
    panic(err)
}

qldbSession := qldbsession.NewFromConfig(cfg, func(options *qldbsession.Options) {
    options.Region = "us-east-1"
})
driver, err := qldbdriver.New(
  "vehicle-registration",
  qldbSession,
  func(options *qldbdriver.DriverOptions) {
    options.LoggerVerbosity = qldbdriver.LogInfo
})
if err != nil {
  panic(err)
}

defer driver.Shutdown(context.Background())
```

------
#### [ 2.x ]

```
awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1")))
qldbSession := qldbsession.New(awsSession)

driver, err := qldbdriver.New(
  "vehicle-registration",
  qldbSession,
  func(options *qldbdriver.DriverOptions) {
    options.LoggerVerbosity = qldbdriver.LogInfo
  })
if err != nil {
  panic(err)
}
```

------

## Operazioni CRUD


QLDB *esegue operazioni di creazione, lettura, aggiornamento ed* eliminazione (CRUD) come parte di una transazione.

**avvertimento**  
Come best practice, 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 in 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 conflitti ottimistici di controllo della concorrenza (OCC).*

[Modello di concorrenza Amazon QLDB](concurrency.md)Per ulteriori informazioni su OCC, vedere.

**Transazioni create implicitamente**

La funzione [QLDBDriver.Execute](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver#QLDBDriver.Execute) accetta una funzione lambda che riceve un'istanza di [Transaction](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver#Transaction), che è possibile utilizzare per eseguire istruzioni. L'istanza di include una `Transaction` transazione creata implicitamente.

È possibile eseguire istruzioni all'interno della funzione lambda utilizzando la funzione. `Transaction.Execute` Il driver esegue implicitamente la transazione quando ritorna la funzione lambda.

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

**Contents**
+ [

### Creazione di tabelle
](#cookbook-golang.crud.creating-tables)
+ [

### Creazione di indici
](#cookbook-golang.crud.creating-indexes)
+ [

### Leggere documenti
](#cookbook-golang.crud.reading)
  + [

#### Utilizzo dei parametri di interrogazione
](#cookbook-golang.reading-using-params)
+ [

### Inserimento di documenti
](#cookbook-golang.crud.inserting)
  + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-golang.crud.inserting.multiple)
+ [

### Aggiornamento dei documenti
](#cookbook-golang.crud.updating)
+ [

### Eliminazione di documenti
](#cookbook-golang.crud.deleting)
+ [

### Esecuzione di più istruzioni in una transazione
](#cookbook-golang.crud.multi-statement)
+ [

### Logica di ripetizione dei tentativi
](#cookbook-golang.crud.retry-logic)
+ [

### Implementazione di vincoli di unicità
](#cookbook-golang.crud.uniqueness-constraints)

### Creazione di tabelle


```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("CREATE TABLE Person")
})
```

### Creazione di indici


```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("CREATE INDEX ON Person(GovId)")
})
```

### Leggere documenti


```
var decodedResult map[string]interface{}

// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName": "Brent" }
_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  result, err := txn.Execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'")
  if err != nil {
    return nil, err
  }
  for result.Next(txn) {
    ionBinary := result.GetCurrentData()
    err = ion.Unmarshal(ionBinary, &decodedResult)
    if err != nil {
      return nil, err
    }
    fmt.Println(decodedResult) // prints map[GovId: TOYENC486FH FirstName:Brent]
  }
  if result.Err() != nil {
    return nil, result.Err()
  }
  return nil, nil
})
if err != nil {
  panic(err)
}
```

#### Utilizzo dei parametri di interrogazione


Il seguente esempio di codice utilizza un parametro di query di tipo nativo.

```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("SELECT * FROM Person WHERE GovId = ?", "TOYENC486FH")
})
if err != nil {
  panic(err)
}
```

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

```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", "TOYENC486FH", "Brent")
})
if err != nil {
  panic(err)
}
```

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

```
govIDs := []string{}{"TOYENC486FH", "ROEE1", "YH844"}

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", govIDs...)
})
if err != nil {
  panic(err)
}
```

**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](ql-reference.create-index.md) sul campo per ottimizzare le `GovId` prestazioni. Senza un indice attivo`GovId`, le query possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

### Inserimento di documenti


Il seguente esempio di codice inserisce tipi di dati nativi.

```
_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  // Check if a document with a GovId of TOYENC486FH exists
  // This is critical to make this transaction idempotent
  result, err := txn.Execute("SELECT * FROM Person WHERE GovId = ?", "TOYENC486FH")
  if err != nil {
    return nil, err
  }
  // Check if there are any results
  if result.Next(txn) {
    // Document already exists, no need to insert
  } else {
    person := map[string]interface{}{
      "GovId": "TOYENC486FH",
      "FirstName": "Brent",
    }
    _, err = txn.Execute("INSERT INTO Person ?", person)
    if err != nil {
      return nil, err
    }
  }
  return nil, nil
})
```

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 attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

#### Inserimento di più documenti in un'unica dichiarazione


Per inserire più documenti utilizzando una singola [INSERT](ql-reference.insert.md) istruzione, è possibile passare un parametro di tipo [elenco](driver-working-with-ion.md#driver-ion-list) all'istruzione come segue.

```
// people is a list
txn.Execute("INSERT INTO People ?", people)
```

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

### Aggiornamento dei documenti


Il seguente esempio di codice utilizza tipi di dati nativi.

```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", "John", "TOYENC486FH")
})
```

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

### Eliminazione di documenti


Il seguente esempio di codice utilizza tipi di dati nativi.

```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("DELETE FROM Person WHERE GovId = ?", "TOYENC486FH")
})
```

**Nota**  
In questo esempio, si consiglia di disporre di un indice sul `GovId` campo per ottimizzare le prestazioni. Senza un indice attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC 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.
func InsureCar(driver *qldbdriver.QLDBDriver, vin string) (bool, error) {
    insured, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {

        result, err := txn.Execute(
            "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)
        if err != nil {
            return false, err
        }

        hasNext := result.Next(txn)
        if !hasNext && result.Err() != nil {
            return false, result.Err()
        }

        if hasNext {
            _, err = txn.Execute(
                "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin)
            if err != nil {
                return false, err
            }
            return true, nil
        }
        return false, nil
    })
    if err != nil {
        panic(err)
    }

    return insured.(bool), err
}
```

### Logica di ripetizione dei tentativi


La `Execute` funzione 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). 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 è [ExponentialBackoffStrategy](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver#ExponentialBackoffStrategy)con una base di millisecondi. `10` È possibile impostare la politica di riprova per istanza del driver e anche per transazione utilizzando un'istanza di. [RetryPolicy](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver#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.

```
import (
  "github.com/aws/aws-sdk-go/aws"
  "github.com/aws/aws-sdk-go/aws/session"
  "github.com/aws/aws-sdk-go/service/qldbsession"
  "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)

func main() {
  awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1")))
  qldbSession := qldbsession.New(awsSession)

  // Configuring retry limit to 2
  retryPolicy := qldbdriver.RetryPolicy{MaxRetryLimit: 2}

  driver, err := qldbdriver.New("test-ledger", qldbSession, func(options *qldbdriver.DriverOptions) {
    options.RetryPolicy = retryPolicy
  })
  if err != nil {
    panic(err)
  }

  // Configuring an exponential backoff strategy with base of 20 milliseconds
  retryPolicy = qldbdriver.RetryPolicy{
    MaxRetryLimit: 2,
    Backoff: qldbdriver.ExponentialBackoffStrategy{SleepBase: 20, SleepCap: 4000,
    }}

  driver, err = qldbdriver.New("test-ledger", qldbSession, func(options *qldbdriver.DriverOptions) {
    options.RetryPolicy = retryPolicy
  })
  if err != nil {
    panic(err)
  }
}
```

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 funzione anonima. La `SetRetryPolicy` funzione sostituisce la politica di riprova impostata per l'istanza del driver.

```
import (
  "context"
  "github.com/aws/aws-sdk-go/aws"
  "github.com/aws/aws-sdk-go/aws/session"
  "github.com/aws/aws-sdk-go/service/qldbsession"
  "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)

func main() {
  awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1")))
  qldbSession := qldbsession.New(awsSession)

  // Configuring retry limit to 2
  retryPolicy1 := qldbdriver.RetryPolicy{MaxRetryLimit: 2}

  driver, err := qldbdriver.New("test-ledger", qldbSession, func(options *qldbdriver.DriverOptions) {
    options.RetryPolicy = retryPolicy1
  })
  if err != nil {
    panic(err)
  }

  // Configuring an exponential backoff strategy with base of 20 milliseconds
  retryPolicy2 := qldbdriver.RetryPolicy{
    MaxRetryLimit: 2,
    Backoff: qldbdriver.ExponentialBackoffStrategy{SleepBase: 20, SleepCap: 4000,
    }}

  // Overrides the retry policy set by the driver instance
  driver.SetRetryPolicy(retryPolicy2)

  driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    return txn.Execute("CREATE TABLE Person")
  })
}
```

### Implementazione di vincoli di unicità


QLDB non 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 specificato`GovId`.

1. 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'eccezione relativa al conflitto OCC.

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

```
govID := "TOYENC486FH"

document := map[string]interface{}{
  "GovId":     "TOYENC486FH",
  "FirstName": "Brent",
}

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  // Check if doc with GovId = govID exists
  result, err := txn.Execute("SELECT * FROM Person WHERE GovId = ?", govID)
  if err != nil {
    return nil, err
  }
  // Check if there are any results
  if result.Next(txn) {
    // Document already exists, no need to insert
    return nil, nil
  }
  return txn.Execute("INSERT INTO Person ?", document)
})
if err != nil {
  panic(err)
}
```

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

## Lavorare con Amazon Ion


Le seguenti sezioni mostrano come utilizzare il modulo Amazon Ion per elaborare i dati Ion.

**Contents**
+ [

### Importazione del modulo Ion
](#cookbook-golang.ion.import)
+ [

### Creazione di tipi di ioni
](#cookbook-golang.ion.creating-types)
+ [

### Ottenere il binario Ion
](#cookbook-golang.ion.getting-binary)
+ [

### Ricevere testo Ion
](#cookbook-golang.ion.getting-text)

### Importazione del modulo Ion


```
import "github.com/amzn/ion-go/ion"
```

### Creazione di tipi di ioni


La libreria Ion per Go attualmente non supporta il Document Object Model (DOM), quindi non puoi creare tipi di dati Ion. Ma puoi eseguire il marshal e il unmarshal tra i tipi nativi Go e il binario Ion quando lavori con QLDB.

### Ottenere il binario Ion


```
aDict := map[string]interface{}{
  "GovId": "TOYENC486FH",
  "FirstName": "Brent",
}

ionBytes, err := ion.MarshalBinary(aDict)
if err != nil {
  panic(err)
}

fmt.Println(ionBytes) // prints [224 1 0 234 238 151 129 131 222 147 135 190 144 133 71 111 118 73 100 137 70 105 114 115 116 78 97 109 101 222 148 138 139 84 79 89 69 78 67 52 56 54 70 72 139 133 66 114 101 110 116]
```

### Ricevere testo Ion


```
aDict := map[string]interface{}{
  "GovId": "TOYENC486FH",
  "FirstName": "Brent",
}

ionBytes, err := ion.MarshalText(aDict)
if err != nil {
  panic(err)
}

fmt.Println(string(ionBytes)) // prints {FirstName:"Brent",GovId:"TOYENC486FH"}
```

Per ulteriori informazioni su Ion, consulta la [documentazione di Amazon Ion](http://amzn.github.io/ion-docs/) su GitHub. Per altri esempi di codice sull'utilizzo di Ion in QLDB, vedere. [Utilizzo dei tipi di dati Amazon Ion in Amazon QLDB](driver-working-with-ion.md)

# Driver Amazon QLDB per Node.js
Driver Node.js

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Per utilizzare i dati nel registro, puoi connetterti ad Amazon QLDB dall'applicazione Node.js utilizzando un driver fornito. AWS Negli argomenti seguenti viene descritto come iniziare a utilizzare il driver QLDB per Node.js.

**Topics**
+ [

## Risorse per i driver
](#getting-started.nodejs.resources)
+ [

## Prerequisiti
](#getting-started.nodejs.prereqs)
+ [

## Installazione
](#getting-started.nodejs.install)
+ [

## Consigli di configurazione
](#nodejs-setup)
+ [Tutorial di avvio rapido](driver-quickstart-nodejs.md)
+ [Riferimento al libro di cucina](driver-cookbook-nodejs.md)

## Risorse per i driver


Per ulteriori informazioni sulle funzionalità supportate dal driver Node.js, consultate le seguenti risorse:
+ [Riferimento API: [3.x, 2.x](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/3.1.0/index.html)[, 1.x](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/index.html)](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/1.0.0/index.html)
+ [Codice sorgente del driver () GitHub](https://github.com/awslabs/amazon-qldb-driver-nodejs)
+ [Codice sorgente dell'applicazione di esempio (GitHub)](https://github.com/aws-samples/amazon-qldb-dmv-sample-nodejs)
+ [Esempi di codice Amazon Ion](ion.code-examples.md)
+ [Crea una semplice operazione CRUD e un flusso di dati su QLDB usando (Blog AWS Lambda )AWS](https://aws.amazon.com/blogs/database/build-a-simple-crud-operation-and-data-stream-on-amazon-qldb-using-aws-lambda/)

## Prerequisiti


Prima di iniziare a utilizzare il driver QLDB per Node.js, è necessario effettuare le seguenti operazioni:

1. Segui le istruzioni di AWS configurazione riportate in. [Accesso ad Amazon QLDB](accessing.md) Questo include gli output seguenti:

   1. Iscriviti a AWS.

   1. Crea un utente con le autorizzazioni QLDB appropriate.

   1. Concedi l'accesso programmatico per lo sviluppo.

1. Installa Node.js versione 14.x o successiva dal sito di [download Node.js](https://nodejs.org/en/download/). (Le versioni precedenti del driver supportano la versione 10.x o successiva di Node.js).

1. Configura il tuo ambiente di sviluppo per l'[AWS SDK per Node.js JavaScript ](https://aws.amazon.com/sdk-for-node-js):

   1. Configura le tue AWS credenziali. Ti consigliamo di creare un file di credenziali condiviso.

      *Per istruzioni, consulta [Caricamento delle credenziali in Node.js dal file delle credenziali condivise nella Guida](https://docs.aws.amazon.com/sdk-for-javascript/latest/developer-guide/loading-node-credentials-shared.html) per gli AWS SDK per JavaScript sviluppatori.*

   1. Imposta il tuo valore predefinito. Regione AWS Per sapere come, vedi [Impostazione di Regione AWS](https://docs.aws.amazon.com/sdk-for-javascript/latest/developer-guide/setting-region.html).

      Per un elenco completo delle regioni disponibili, consulta gli [endpoint e le quote di Amazon QLDB](https://docs.aws.amazon.com/general/latest/gr/qldb.html) nel. *Riferimenti generali di AWS*

Successivamente, puoi scaricare l'applicazione di esempio completa del tutorial oppure puoi installare solo il driver in un progetto Node.js ed eseguire brevi esempi di codice.
+ Per installare il driver QLDB e AWS l'SDK JavaScript per Node.js in un progetto esistente, procedi a. [Installazione](#getting-started.nodejs.install)
+ Per configurare un progetto ed eseguire brevi esempi di codice che illustrano le transazioni di dati di base su un registro, consulta la. [Tutorial di avvio rapido](driver-quickstart-nodejs.md)
+ Per eseguire esempi più approfonditi delle operazioni delle API relative ai dati e alla gestione nell'applicazione di esempio completa del tutorial, consulta il. [Tutorial su Node.js](getting-started.nodejs.tutorial.md)

## Installazione


QLDB supporta le seguenti versioni dei driver e le relative dipendenze da Node.js.


****  

| Versione driver | Node.js version (Versione Node.js) | Stato | Data di rilascio più recente | 
| --- | --- | --- | --- | 
| [1.x](https://www.npmjs.com/package/amazon-qldb-driver-nodejs/v/1.0.0) | 10.x o versione successiva | Versione di produzione | 5 giugno 2020 | 
|  [2.x](https://www.npmjs.com/package/amazon-qldb-driver-nodejs/v/2.2.0) | 10.x o versione successiva | Versione di produzione | 6 maggio 2021 | 
| [3.x](https://www.npmjs.com/package/amazon-qldb-driver-nodejs/v/3.1.0) | 14.x o versione successiva | Versione di produzione | 10 novembre 2023 | 

Per installare il driver QLDB [usando npm (il gestore di pacchetti Node.js), inserisci il](https://www.npmjs.com/) seguente comando dalla directory principale del tuo progetto.

------
#### [ 3.x ]

```
npm install amazon-qldb-driver-nodejs
```

------
#### [ 2.x ]

```
npm install amazon-qldb-driver-nodejs@2.2.0
```

------
#### [ 1.x ]

```
npm install amazon-qldb-driver-nodejs@1.0.0
```

------

Il driver ha dipendenze peer-to-peer dai seguenti pacchetti. È inoltre necessario installare questi pacchetti come dipendenze nel progetto.

------
#### [ 3.x ]

Client QLDB aggregato modulare (API di gestione)

```
npm install @aws-sdk/client-qldb
```

Client di sessione *QLDB aggregato modulare (*API dati)

```
npm install @aws-sdk/client-qldb-session
```

Formato dati Amazon Ion

```
npm install ion-js
```

 JavaScript Implementazione pura di `BigInt`

```
npm install jsbi
```

------
#### [ 2.x ]

AWS SDK per JavaScript

```
npm install aws-sdk
```

Formato dati Amazon Ion

```
npm install ion-js@4.0.0
```

 JavaScript Implementazione pura di `BigInt`

```
npm install jsbi@3.1.1
```

------
#### [ 1.x ]

AWS SDK per JavaScript

```
npm install aws-sdk
```

Formato dati Amazon Ion

```
npm install ion-js@4.0.0
```

 JavaScript Implementazione pura di `BigInt`

```
npm install jsbi@3.1.1
```

------

**Utilizzo del driver per connettersi a un registro**

Quindi puoi importare il driver e usarlo per connetterti a un libro mastro. Il seguente esempio di TypeScript codice mostra come creare un'istanza di driver per un nome di registro specificato e. Regione AWS

------
#### [ 3.x ]

```
import { Agent } from 'https';
import { QLDBSessionClientConfig } from "@aws-sdk/client-qldb-session";
import { QldbDriver, RetryConfig  } from 'amazon-qldb-driver-nodejs';
import { NodeHttpHandlerOptions } from "@aws-sdk/node-http-handler";

const maxConcurrentTransactions: number = 10;
const retryLimit: number = 4;

//Reuse connections with keepAlive
const lowLevelClientHttpOptions: NodeHttpHandlerOptions = {
    httpAgent: new Agent({
      maxSockets: maxConcurrentTransactions
    })
};

const serviceConfigurationOptions: QLDBSessionClientConfig = {
    region: "us-east-1"
};

//Use driver's default backoff function for this example (no second parameter provided to RetryConfig)
const retryConfig: RetryConfig = new RetryConfig(retryLimit);
const qldbDriver: QldbDriver = new QldbDriver("testLedger", serviceConfigurationOptions, lowLevelClientHttpOptions, maxConcurrentTransactions, retryConfig);

qldbDriver.getTableNames().then(function(tableNames: string[]) {
    console.log(tableNames);
});
```

------
#### [ 2.x ]

```
import { Agent } from 'https';
import { QldbDriver, RetryConfig  } from 'amazon-qldb-driver-nodejs';

const maxConcurrentTransactions: number = 10;
const retryLimit: number = 4;

//Reuse connections with keepAlive
const agentForQldb: Agent = new Agent({
    keepAlive: true,
    maxSockets: maxConcurrentTransactions
});

const serviceConfigurationOptions = {
    region: "us-east-1",
    httpOptions: {
        agent: agentForQldb
    }
};

//Use driver's default backoff function for this example (no second parameter provided to RetryConfig)
const retryConfig: RetryConfig = new RetryConfig(retryLimit);
const qldbDriver: QldbDriver = new QldbDriver("testLedger", serviceConfigurationOptions, maxConcurrentTransactions, retryConfig);

qldbDriver.getTableNames().then(function(tableNames: string[]) {
    console.log(tableNames);
});
```

------
#### [ 1.x ]

```
import { Agent } from 'https';
import { QldbDriver } from 'amazon-qldb-driver-nodejs';

const poolLimit: number = 10;
const retryLimit: number = 4;

//Reuse connections with keepAlive
const agentForQldb: Agent = new Agent({
    keepAlive: true,
    maxSockets: poolLimit
});

const serviceConfigurationOptions = {
    region: "us-east-1",
    httpOptions: {
        agent: agentForQldb
    }
};

const qldbDriver: QldbDriver = new QldbDriver("testLedger", serviceConfigurationOptions, retryLimit, poolLimit);
qldbDriver.getTableNames().then(function(tableNames: string[]) {
    console.log(tableNames);
});
```

------

Per brevi esempi di codice su come eseguire transazioni di dati di base su un registro, vedere. [Riferimento al libro di cucina](driver-cookbook-nodejs.md)

## Consigli di configurazione


### Riutilizzo delle connessioni con keep-alive


#### Driver QLDB Node.js versione 3


L'agente HTTP/HTTPS Node.js predefinito crea una nuova connessione TCP per ogni nuova richiesta. Per evitare il costo di stabilire una nuova connessione, la versione AWS SDK per JavaScript v3 riutilizza le connessioni TCP per impostazione predefinita. *Per ulteriori informazioni e per scoprire come disabilitare il riutilizzo delle connessioni, consulta [Riutilizzo delle connessioni con keep-alive](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-reusing-connections.html) in Node.js nella Guida per gli sviluppatori.AWS SDK per JavaScript *

Si consiglia di utilizzare l'impostazione predefinita per riutilizzare le connessioni nel driver QLDB per Node.js. Durante l'inizializzazione del driver, imposta l'opzione HTTP del client di basso livello sullo stesso valore `maxSockets` per cui hai impostato. `maxConcurrentTransactions`

Ad esempio, vedete quanto segue JavaScript o TypeScript codice.

------
#### [ JavaScript ]

```
const qldb = require('amazon-qldb-driver-nodejs');
const https = require('https');

//Replace this value as appropriate for your application
const maxConcurrentTransactions = 50;

const agentForQldb = new https.Agent({
    //Set this to the same value as `maxConcurrentTransactions`(previously called `poolLimit`)
    //Do not rely on the default value of `Infinity`
    "maxSockets": maxConcurrentTransactions
});

const lowLevelClientHttpOptions = {
    httpAgent: agentForQldb
}

let driver = new qldb.QldbDriver("testLedger", undefined, lowLevelClientHttpOptions, maxConcurrentTransactions);
```

------
#### [ TypeScript ]

```
import { Agent } from 'https';
import { NodeHttpHandlerOptions } from "@aws-sdk/node-http-handler";
import { QldbDriver } from 'amazon-qldb-driver-nodejs';

//Replace this value as appropriate for your application
const maxConcurrentTransactions: number = 50;

const agentForQldb: Agent = new Agent({
    //Set this to the same value as `maxConcurrentTransactions`(previously called `poolLimit`)
    //Do not rely on the default value of `Infinity`
    maxSockets: maxConcurrentTransactions
});

const lowLevelClientHttpOptions: NodeHttpHandlerOptions = {
    httpAgent: agentForQldb
};

let driver = new QldbDriver("testLedger", undefined, lowLevelClientHttpOptions, maxConcurrentTransactions);
```

------

#### Driver QLDB Node.js versione 2


L'agente HTTP/HTTPS Node.js predefinito crea una nuova connessione TCP per ogni nuova richiesta. Per evitare il costo di stabilire una nuova connessione, si consiglia di riutilizzare una connessione esistente.

Per riutilizzare le connessioni nel driver QLDB per Node.js, utilizzare una delle seguenti opzioni:
+ Durante l'inizializzazione del driver, imposta le seguenti opzioni HTTP del client di basso livello:
  + `keepAlive` – `true`
  + `maxSockets`— Lo stesso valore che hai impostato per `maxConcurrentTransactions`

  Ad esempio, vedere quanto segue JavaScript o TypeScript codice.

------
#### [ JavaScript ]

  ```
  const qldb = require('amazon-qldb-driver-nodejs');
  const https = require('https');
  
  //Replace this value as appropriate for your application
  const maxConcurrentTransactions = 50;
  
  const agentForQldb = new https.Agent({
      "keepAlive": true,
      //Set this to the same value as `maxConcurrentTransactions`(previously called `poolLimit`)
      //Do not rely on the default value of `Infinity`
      "maxSockets": maxConcurrentTransactions
  });
  
  const serviceConfiguration = { "httpOptions": {
      "agent": agentForQldb
  }};
  
  let driver = new qldb.QldbDriver("testLedger", serviceConfiguration, maxConcurrentTransactions);
  ```

------
#### [ TypeScript ]

  ```
  import { Agent } from 'https';
  import { ClientConfiguration } from 'aws-sdk/clients/acm';
  import { QldbDriver } from 'amazon-qldb-driver-nodejs';
  
  //Replace this value as appropriate for your application
  const maxConcurrentTransactions: number = 50;
  
  const agentForQldb: Agent = new Agent({
      keepAlive: true,
      //Set this to the same value as `maxConcurrentTransactions`(previously called `poolLimit`)
      //Do not rely on the default value of `Infinity`
      maxSockets: maxConcurrentTransactions
  });
  
  const serviceConfiguration: ClientConfiguration = { httpOptions: {
      agent: agentForQldb
  }};
  
  let driver = new QldbDriver("testLedger", serviceConfiguration, maxConcurrentTransactions);
  ```

------
+ In alternativa, è possibile impostare la variabile di `AWS_NODEJS_CONNECTION_REUSE_ENABLED` ambiente su`1`. *Per ulteriori informazioni, consulta [Riutilizzo delle connessioni con Keep-Alive in Node.js nella Guida](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html) per gli AWS SDK per JavaScript sviluppatori.*
**Nota**  
Se imposti questa variabile di ambiente, influirà su tutti quelli Servizi AWS che utilizzano il. AWS SDK per JavaScript

# Driver Amazon QLDB per Node.js — Tutorial di avvio rapido
Tutorial di avvio rapido

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo tutorial, imparerai come configurare una semplice applicazione utilizzando il driver Amazon QLDB per Node.js. Questa guida include i passaggi per l'installazione del driver ed esempi brevi JavaScript e in TypeScript codice delle operazioni di base di *creazione, lettura, aggiornamento ed eliminazione* (CRUD). Per esempi più approfonditi che illustrano queste operazioni in un'applicazione di esempio completa, consulta la. [Tutorial su Node.js](getting-started.nodejs.tutorial.md)

**Nota**  
Ove applicabile, alcuni passaggi contengono esempi di codice diversi per ogni versione principale supportata del driver QLDB per Node.js.

**Topics**
+ [

## Prerequisiti
](#driver-quickstart-nodejs.prereqs)
+ [

## Fase 1: Configurazione del progetto
](#driver-quickstart-nodejs.step-1)
+ [

## Fase 2: Inizializzare il driver
](#driver-quickstart-nodejs.step-2)
+ [

## Fase 3: Creare una tabella e un indice
](#driver-quickstart-nodejs.step-3)
+ [

## Fase 4: Inserimento di un documento
](#driver-quickstart-nodejs.step-4)
+ [

## Fase 5: Interroga il documento
](#driver-quickstart-nodejs.step-5)
+ [

## Fase 6: Aggiornare il documento
](#driver-quickstart-nodejs.step-6)
+ [

## Esecuzione dell'applicazione completa
](#driver-quickstart-nodejs.complete)

## Prerequisiti


Prima di iniziare, assicurati di fare quanto segue:

1. Completa il driver [Prerequisiti](getting-started.nodejs.md#getting-started.nodejs.prereqs) per il Node.js, se non l'hai già fatto. Ciò include la registrazione AWS, la concessione dell'accesso programmatico per lo sviluppo e l'installazione di Node.js.

1. Crea un libro mastro denominato. `quick-start`

   Per informazioni su come creare un registro, consulta [Operazioni di base per i registri Amazon QLDB](ledger-management.basics.md) o [Fase 1: Creare un nuovo libro contabile](getting-started-step-1.md) in *Guida introduttiva alla console*.

Se lo utilizzi TypeScript, devi anche eseguire i seguenti passaggi di configurazione.

### Usando TypeScript


**Per installare TypeScript**

1. Installa il TypeScript pacchetto. Il driver QLDB funziona sulla versione 3.8.x. TypeScript 

   ```
   $ npm install --global typescript@3.8.0
   ```

1. Dopo aver installato il pacchetto, esegui il comando seguente per assicurarti che il TypeScript compilatore sia installato.

   ```
   $ tsc --version
   ```

Per eseguire il codice nei passaggi seguenti, tenete presente che dovete prima trasporre il TypeScript file in JavaScript codice eseguibile, come segue.

```
$ tsc app.ts; node app.js
```

## Fase 1: Configurazione del progetto


Innanzitutto, configurate il progetto Node.js.

1. Crea una cartella per la tua applicazione.

   ```
   $ mkdir myproject
   $ cd myproject
   ```

1. Per inizializzare il progetto, immettete il seguente `npm` comando e rispondete alle domande che vi vengono poste durante la configurazione. Puoi usare i valori predefiniti per la maggior parte delle domande.

   ```
   $ npm init
   ```

1. Installa il driver Amazon QLDB per Node.js.
   + Utilizzando la versione 3.x

     ```
     $ npm install amazon-qldb-driver-nodejs --save
     ```
   + Utilizzando la versione 2.x

     ```
     $ npm install amazon-qldb-driver-nodejs@2.2.0 --save
     ```
   + Utilizzando la versione 1.x

     ```
     $ npm install amazon-qldb-driver-nodejs@1.0.0 --save
     ```

1. Installa le dipendenze peer del driver.
   + Utilizzando la versione 3.x

     ```
     $ npm install @aws-sdk/client-qldb-session --save
     $ npm install ion-js --save
     $ npm install jsbi --save
     ```
   + Utilizzando la versione 2.x o 1.x

     ```
     $ npm install aws-sdk --save
     $ npm install ion-js@4.0.0 --save
     $ npm install jsbi@3.1.1 --save
     ```

1. Crea un nuovo file denominato `app.js` for JavaScript o `app.ts` for. TypeScript

   Quindi, aggiungi in modo incrementale gli esempi di codice nei passaggi seguenti per provare alcune operazioni CRUD di base. [In alternativa, puoi saltare il step-by-step tutorial ed eseguire invece l'applicazione completa.](#driver-quickstart-nodejs.complete)

## Fase 2: Inizializzare il driver


Inizializza un'istanza del driver che si connette al registro denominato. `quick-start` Aggiungi il codice seguente al tuo file `app.js` or`app.ts`.

### Utilizzando la versione 3.x


------
#### [ JavaScript ]

```
var qldb = require('amazon-qldb-driver-nodejs');
var https = require('https');

function main() {
    const maxConcurrentTransactions = 10;
    const retryLimit = 4;

    const agentForQldb = new https.Agent({
        maxSockets: maxConcurrentTransactions
    });

    const lowLevelClientHttpOptions = {
       httpAgent: agentForQldb
    }

    const serviceConfigurationOptions = {
        region: "us-east-1"
    };

    // Use driver's default backoff function for this example (no second parameter provided to RetryConfig)
    var retryConfig = new qldb.RetryConfig(retryLimit);
    var driver = new qldb.QldbDriver("quick-start", serviceConfigurationOptions, lowlevelClientHttpOptions, maxConcurrentTransactions, retryConfig);
}

main();
```

------
#### [ TypeScript ]

```
import { Agent } from "https";
import { NodeHttpHandlerOptions } from "@aws-sdk/node-http-handler";
import { QLDBSessionClientConfig } from "@aws-sdk/client-qldb-session";
import { QldbDriver, RetryConfig } from "amazon-qldb-driver-nodejs";

function main(): void {
    const maxConcurrentTransactions: number = 10;
    const agentForQldb: Agent = new Agent({
        maxSockets: maxConcurrentTransactions
    });

    const lowLevelClientHttpOptions: NodeHttpHandlerOptions = {
      httpAgent: agentForQldb
    };

    const serviceConfigurationOptions: QLDBSessionClientConfig = {
        region: "us-east-1"
    };

    const retryLimit: number = 4;
    // Use driver's default backoff function for this example (no second parameter provided to RetryConfig)
    const retryConfig: RetryConfig = new RetryConfig(retryLimit);
    const driver: QldbDriver = new QldbDriver("quick-start", serviceConfigurationOptions, lowLevelClientHttpOptions, maxConcurrentTransactions, retryConfig);
}

if (require.main === module) {
    main();
}
```

------

**Nota**  
In questo esempio di codice, sostituiscilo *us-east-1* con il Regione AWS punto in cui hai creato il libro mastro.
Per semplicità, i restanti esempi di codice di questa guida utilizzano un driver con impostazioni predefinite, come specificato nell'esempio seguente per la versione 1.x. In `RetryConfig` alternativa, è possibile utilizzare un'istanza di driver personalizzata anche con un'istanza personalizzata.

### Utilizzando la versione 2.x


------
#### [ JavaScript ]

```
var qldb = require('amazon-qldb-driver-nodejs');
var https = require('https');

function main() {
    var maxConcurrentTransactions = 10;
    var retryLimit = 4;

    var agentForQldb = new https.Agent({
        keepAlive: true,
        maxSockets: maxConcurrentTransactions
    });

    var serviceConfigurationOptions = {
        region: "us-east-1",
        httpOptions: {
            agent: agentForQldb
        }
    };

    // Use driver's default backoff function for this example (no second parameter provided to RetryConfig)
    var retryConfig = new qldb.RetryConfig(retryLimit);
    var driver = new qldb.QldbDriver("quick-start", serviceConfigurationOptions, maxConcurrentTransactions, retryConfig);
}

main();
```

------
#### [ TypeScript ]

```
import { QldbDriver, RetryConfig } from "amazon-qldb-driver-nodejs";
import { ClientConfiguration } from "aws-sdk/clients/acm";
import { Agent } from "https";

function main(): void {
    const maxConcurrentTransactions: number = 10;
    const agentForQldb: Agent = new Agent({
        keepAlive: true,
        maxSockets: maxConcurrentTransactions
    });
    const serviceConfigurationOptions: ClientConfiguration = {
        region: "us-east-1",
        httpOptions: {
            agent: agentForQldb
        }
    };
    const retryLimit: number = 4;
    // Use driver's default backoff function for this example (no second parameter provided to RetryConfig)
    const retryConfig: RetryConfig = new RetryConfig(retryLimit);
    const driver: QldbDriver = new QldbDriver("quick-start", serviceConfigurationOptions, maxConcurrentTransactions, retryConfig);
}

if (require.main === module) {
    main();
}
```

------

**Nota**  
In questo esempio di codice, sostituiscilo *us-east-1* con il Regione AWS punto in cui hai creato il libro mastro.
La versione 2.x introduce il nuovo parametro `RetryConfig` opzionale per l'inizializzazione. `QldbDriver`
Per semplicità, gli altri esempi di codice in questa guida utilizzano un driver con impostazioni predefinite, come specificato nell'esempio seguente per la versione 1.x. In `RetryConfig` alternativa, è possibile utilizzare un'istanza di driver personalizzata anche con un'istanza personalizzata.
Questo esempio di codice inizializza un driver che riutilizza le connessioni esistenti impostando le opzioni keep-alive. Per ulteriori informazioni, [Consigli di configurazione](getting-started.nodejs.md#nodejs-setup) consultate il driver Node.js.

### Utilizzo della versione 1.x


------
#### [ JavaScript ]

```
const qldb = require('amazon-qldb-driver-nodejs');

function main() {
    // Use default settings
    const driver = new qldb.QldbDriver("quick-start");
}

main();
```

------
#### [ TypeScript ]

```
import { QldbDriver } from "amazon-qldb-driver-nodejs";

function main(): void {
    // Use default settings
    const driver: QldbDriver = new QldbDriver("quick-start");
}

if (require.main === module) {
    main();
}
```

------

**Nota**  
È possibile impostare la variabile di `AWS_REGION` ambiente per specificare la regione. Per ulteriori informazioni, consulta [Impostazione di Regione AWS nella](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-region.html) *Guida per gli AWS SDK per JavaScript sviluppatori*.

## Fase 3: Creare una tabella e un indice


I seguenti esempi di codice mostrano come eseguire le `CREATE INDEX` istruzioni `CREATE TABLE` e le istruzioni.

1. Aggiungete la seguente funzione che crea una tabella denominata`People`.

------
#### [ JavaScript ]

   ```
   async function createTable(txn) {
       await txn.execute("CREATE TABLE People");
   }
   ```

------
#### [ TypeScript ]

   ```
   async function createTable(txn: TransactionExecutor): Promise<void> {
       await txn.execute("CREATE TABLE People");
   }
   ```

------

1. Aggiungete la seguente funzione che crea un indice per il `firstName` campo della `People` tabella. [Gli indici](ql-reference.create-index.md) sono necessari per ottimizzare le prestazioni delle query e aiutano a limitare le eccezioni ai conflitti [OCC (Optimistic Concurrency Control)](concurrency.md).

------
#### [ JavaScript ]

   ```
   async function createIndex(txn) {
       await txn.execute("CREATE INDEX ON People (firstName)");
   }
   ```

------
#### [ TypeScript ]

   ```
   async function createIndex(txn: TransactionExecutor): Promise<void> {
       await txn.execute("CREATE INDEX ON People (firstName)");
   }
   ```

------

1. Nella `main` funzione, si chiama prima e poi si chiama. `createTable` `createIndex`

------
#### [ JavaScript ]

   ```
   const qldb = require('amazon-qldb-driver-nodejs');
   
   async function main() {
       // Use default settings
       const driver = new qldb.QldbDriver("quick-start");
   
       await driver.executeLambda(async (txn) => {
           console.log("Create table People");
           await createTable(txn);
           console.log("Create index on firstName");
           await createIndex(txn);
       });
   
       driver.close();
   }
   
   main();
   ```

------
#### [ TypeScript ]

   ```
   import { QldbDriver, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   
   async function main(): Promise<void> {
       // Use default settings
       const driver: QldbDriver = new QldbDriver("quick-start");
   
       await driver.executeLambda(async (txn: TransactionExecutor) => {
           console.log("Create table People");
           await createTable(txn);
           console.log("Create index on firstName");
           await createIndex(txn);
       });
   
       driver.close();
   }
   
   if (require.main === module) {
       main();
   }
   ```

------

1. Esegui il codice per creare la tabella e l'indice.

------
#### [ JavaScript ]

   ```
   $ node app.js
   ```

------
#### [ TypeScript ]

   ```
   $ tsc app.ts; node app.js
   ```

------

## Fase 4: Inserimento di un documento


Il seguente esempio di codice mostra come eseguire un'`INSERT`istruzione. QLDB supporta il linguaggio di interrogazione [PartiQL](ql-reference.md) (compatibile con SQL) e il formato dati [Amazon](ion.md) Ion (superset di JSON).

1. Aggiungi la seguente funzione che inserisce un documento nella tabella. `People`

------
#### [ JavaScript ]

   ```
   async function insertDocument(txn) {
       const person = {
           firstName: "John",
           lastName: "Doe",
           age: 42
       };
       await txn.execute("INSERT INTO People ?", person);
   }
   ```

------
#### [ TypeScript ]

   ```
   async function insertDocument(txn: TransactionExecutor): Promise<void> {
       const person: Record<string, any> = {
           firstName: "John",
           lastName: "Doe",
           age: 42
       };
       await txn.execute("INSERT INTO People ?", person);
   }
   ```

------

   Questo esempio utilizza un punto interrogativo (`?`) come segnaposto variabile per passare le informazioni del documento all'istruzione. Il `execute` metodo supporta valori sia nei tipi Amazon Ion che nei tipi nativi Node.js.
**Suggerimento**  
Per inserire più documenti utilizzando una singola [INSERT](ql-reference.insert.md) istruzione, puoi passare un parametro di tipo [elenco](driver-working-with-ion.md#driver-ion-list) all'istruzione come segue.  

   ```
   // people is a list
   txn.execute("INSERT INTO People ?", people);
   ```
Non racchiudete la variabile placeholder (`?`) tra parentesi angolari doppie (`<<...>>`) quando passate un elenco. *Nelle istruzioni PartiQL manuali, le parentesi doppie angolari indicano una raccolta non ordinata nota come borsa.*

1. Nella `main` funzione, rimuovi le `createIndex` chiamate `createTable` and e aggiungi una chiamata a. `insertDocument`

------
#### [ JavaScript ]

   ```
   const qldb = require('amazon-qldb-driver-nodejs');
   
   async function main() {
       // Use default settings
       const driver = new qldb.QldbDriver("quick-start");
   
       await driver.executeLambda(async (txn) => {
           console.log("Insert document");
           await insertDocument(txn);
       });
   
       driver.close();
   }
   
   main();
   ```

------
#### [ TypeScript ]

   ```
   import { QldbDriver, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   
   async function main(): Promise<void> {
       // Use default settings
       const driver: QldbDriver = new QldbDriver("quick-start");
   
       await driver.executeLambda(async (txn: TransactionExecutor) => {
           console.log("Insert document");
           await insertDocument(txn);
       });
   
       driver.close();
   }
   
   if (require.main === module) {
       main();
   }
   ```

------

## Fase 5: Interroga il documento


Il seguente esempio di codice mostra come eseguire un'`SELECT`istruzione.

1. Aggiungete la seguente funzione che interroga un documento dalla `People` tabella.

------
#### [ JavaScript ]

   ```
   async function fetchDocuments(txn) {
       return await txn.execute("SELECT firstName, age, lastName FROM People WHERE firstName = ?", "John");
   }
   ```

------
#### [ TypeScript ]

   ```
   async function fetchDocuments(txn: TransactionExecutor): Promise<dom.Value[]> {
       return (await txn.execute("SELECT firstName, age, lastName FROM People WHERE firstName = ?", "John")).getResultList();
   }
   ```

------

1. Nella `main` funzione, aggiungi la seguente chiamata a `fetchDocuments` dopo la chiamata a`insertDocument`.

------
#### [ JavaScript ]

   ```
   const qldb = require('amazon-qldb-driver-nodejs');
   
   async function main() {
       // Use default settings
       const driver = new qldb.QldbDriver("quick-start");
   
       var resultList = await driver.executeLambda(async (txn) => {
           console.log("Insert document");
           await insertDocument(txn);
           console.log("Fetch document");
           var result = await fetchDocuments(txn);
           return result.getResultList();
       });
   
       // Pretty print the result list
       console.log("The result List is ", JSON.stringify(resultList, null, 2));
       driver.close();
   }
   
   main();
   ```

------
#### [ TypeScript ]

   ```
   import { QldbDriver, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   import { dom } from "ion-js";
   
   async function main(): Promise<void> {
       // Use default settings
       const driver: QldbDriver = new QldbDriver("quick-start");
   
       const resultList: dom.Value[] = await driver.executeLambda(async (txn: TransactionExecutor) => {
           console.log("Insert document");
           await insertDocument(txn);
           console.log("Fetch document");
           return await fetchDocuments(txn);
       });
   
       // Pretty print the result list
       console.log("The result List is ", JSON.stringify(resultList, null, 2));
       driver.close();
   }
   
   if (require.main === module) {
       main();
   }
   ```

------

## Fase 6: Aggiornare il documento


Il seguente esempio di codice mostra come eseguire un'`UPDATE`istruzione.

1. Aggiungere la seguente funzione che aggiorna un documento nella `People` tabella modificandolo `lastName` in`"Stiles"`.

------
#### [ JavaScript ]

   ```
   async function updateDocuments(txn) {
       await txn.execute("UPDATE People SET lastName = ? WHERE firstName = ?", "Stiles", "John");
   }
   ```

------
#### [ TypeScript ]

   ```
   async function updateDocuments(txn: TransactionExecutor): Promise<void> {
       await txn.execute("UPDATE People SET lastName = ? WHERE firstName = ?", "Stiles", "John");
   }
   ```

------

1. Nella `main` funzione, aggiungi la seguente chiamata a `updateDocuments` dopo la chiamata a`fetchDocuments`. Quindi, chiama `fetchDocuments` di nuovo per vedere i risultati aggiornati.

------
#### [ JavaScript ]

   ```
   const qldb = require('amazon-qldb-driver-nodejs');
   
   async function main() {
       // Use default settings
       const driver = new qldb.QldbDriver("quick-start");
   
       var resultList = await driver.executeLambda(async (txn) => {
           console.log("Insert document");
           await insertDocument(txn);
           console.log("Fetch document");
           await fetchDocuments(txn);
           console.log("Update document");
           await updateDocuments(txn);
           console.log("Fetch document after update");
           var result = await fetchDocuments(txn);
           return result.getResultList();
       });
   
       // Pretty print the result list
       console.log("The result List is ", JSON.stringify(resultList, null, 2));
       driver.close();
   }
   
   main();
   ```

------
#### [ TypeScript ]

   ```
   import { QldbDriver, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   import { dom } from "ion-js";
   
   async function main(): Promise<void> {
       // Use default settings
       const driver: QldbDriver = new QldbDriver("quick-start");
   
       const resultList: dom.Value[] = await driver.executeLambda(async (txn: TransactionExecutor) => {
           console.log("Insert document");
           await insertDocument(txn);
           console.log("Fetch document");
           await fetchDocuments(txn);
           console.log("Update document");
           await updateDocuments(txn);
           console.log("Fetch document after update");
           return await fetchDocuments(txn);
       });
   
       // Pretty print the result list
       console.log("The result List is ", JSON.stringify(resultList, null, 2));
       driver.close();
   }
   
   if (require.main === module) {
       main();
   }
   ```

------

1. Esegui il codice per inserire, interrogare e aggiornare un documento.

------
#### [ JavaScript ]

   ```
   $ node app.js
   ```

------
#### [ TypeScript ]

   ```
   $ tsc app.ts; node app.js
   ```

------

## Esecuzione dell'applicazione completa


I seguenti esempi di codice sono le versioni complete di `app.js` and`app.ts`. Invece di eseguire i passaggi precedenti singolarmente, puoi anche eseguire questo codice dall'inizio alla fine. Questa applicazione dimostra alcune operazioni CRUD di base sul registro denominato. `quick-start`

**Nota**  
Prima di eseguire questo codice, assicuratevi di non avere già una tabella attiva denominata `People` nel registro. `quick-start`

------
#### [ JavaScript ]

```
const qldb = require('amazon-qldb-driver-nodejs');

async function createTable(txn) {
    await txn.execute("CREATE TABLE People");
}

async function createIndex(txn) {
    await txn.execute("CREATE INDEX ON People (firstName)");
}

async function insertDocument(txn) {
    const person = {
        firstName: "John",
        lastName: "Doe",
        age: 42
    };
    await txn.execute("INSERT INTO People ?", person);
}

async function fetchDocuments(txn) {
    return await txn.execute("SELECT firstName, age, lastName FROM People WHERE firstName = ?", "John");
}

async function updateDocuments(txn) {
    await txn.execute("UPDATE People SET lastName = ? WHERE firstName = ?", "Stiles", "John");
}

async function main() {
    // Use default settings
    const driver = new qldb.QldbDriver("quick-start");

    var resultList = await driver.executeLambda(async (txn) => {
        console.log("Create table People");
        await createTable(txn);
        console.log("Create index on firstName");
        await createIndex(txn);
        console.log("Insert document");
        await insertDocument(txn);
        console.log("Fetch document");
        await fetchDocuments(txn);
        console.log("Update document");
        await updateDocuments(txn);
        console.log("Fetch document after update");
        var result = await fetchDocuments(txn);
        return result.getResultList();
    });

    // Pretty print the result list
    console.log("The result List is ", JSON.stringify(resultList, null, 2));
    driver.close();
}

main();
```

------
#### [ TypeScript ]

```
import { QldbDriver, TransactionExecutor } from "amazon-qldb-driver-nodejs";
import { dom } from "ion-js";

async function createTable(txn: TransactionExecutor): Promise<void> {
    await txn.execute("CREATE TABLE People");
}

async function createIndex(txn: TransactionExecutor): Promise<void> {
    await txn.execute("CREATE INDEX ON People (firstName)");
}

async function insertDocument(txn: TransactionExecutor): Promise<void> {
    const person: Record<string, any> = {
        firstName: "John",
        lastName: "Doe",
        age: 42
    };
    await txn.execute("INSERT INTO People ?", person);
}

async function fetchDocuments(txn: TransactionExecutor): Promise<dom.Value[]> {
    return (await txn.execute("SELECT firstName, age, lastName FROM People WHERE firstName = ?", "John")).getResultList();
}

async function updateDocuments(txn: TransactionExecutor): Promise<void> {
    await txn.execute("UPDATE People SET lastName = ? WHERE firstName = ?", "Stiles", "John");
};

async function main(): Promise<void> {
    // Use default settings
    const driver: QldbDriver = new QldbDriver("quick-start");

    const resultList: dom.Value[] = await driver.executeLambda(async (txn: TransactionExecutor) => {
        console.log("Create table People");
        await createTable(txn);
        console.log("Create index on firstName");
        await createIndex(txn);
        console.log("Insert document");
        await insertDocument(txn);
        console.log("Fetch document");
        await fetchDocuments(txn);
        console.log("Update document");
        await updateDocuments(txn);
        console.log("Fetch document after update");
        return await fetchDocuments(txn);
    });

    // Pretty print the result list
    console.log("The result List is ", JSON.stringify(resultList, null, 2));
    driver.close();
}

if (require.main === module) {
    main();
}
```

------

Per eseguire l'applicazione completa, immettete il seguente comando.

------
#### [ JavaScript ]

```
$ node app.js
```

------
#### [ TypeScript ]

```
$ tsc app.ts; node app.js
```

------

# Driver Amazon QLDB per Node.js — Guida al libro di cucina
Riferimento al libro di cucina

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questa guida di riferimento mostra i casi d'uso comuni del driver Amazon QLDB per Node.js. JavaScript Fornisce esempi di TypeScript codice 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à.

**Contents**
+ [

## Importazione del driver
](#cookbook-nodejs.importing)
+ [

## Istanziazione del driver
](#cookbook-nodejs.instantiating)
+ [

## Operazioni CRUD
](#cookbook-nodejs.crud)
  + [

### Creazione di tabelle
](#cookbook-nodejs.crud.creating-tables)
  + [

### Creazione di indici
](#cookbook-nodejs.crud.creating-indexes)
  + [

### Leggere documenti
](#cookbook-nodejs.crud.reading)
    + [

#### Utilizzo dei parametri di interrogazione
](#cookbook-nodejs.reading-using-params)
  + [

### Inserimento di documenti
](#cookbook-nodejs.crud.inserting)
    + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-nodejs.crud.inserting.multiple)
  + [

### Aggiornamento dei documenti
](#cookbook-nodejs.crud.updating)
  + [

### Eliminazione di documenti
](#cookbook-nodejs.crud.deleting)
  + [

### Esecuzione di più istruzioni in una transazione
](#cookbook-nodejs.crud.multi-statement)
  + [

### Logica di ripetizione dei tentativi
](#cookbook-nodejs.crud.retry-logic)
  + [

### Implementazione di vincoli di unicità
](#cookbook-nodejs.crud.uniqueness-constraints)
+ [

## Lavorare con Amazon Ion
](#cookbook-nodejs.ion)
  + [

### Importazione del modulo Ion
](#cookbook-nodejs.ion.import)
  + [

### Creazione di tipi di ioni
](#cookbook-nodejs.ion.creating-types)
  + [

### Ottenere un dump binario Ion
](#cookbook-nodejs.ion.getting-binary)
  + [

### Ottenere un dump di testo Ion
](#cookbook-nodejs.ion.getting-text)

## Importazione del driver


Il seguente esempio di codice importa il driver.

------
#### [ JavaScript ]

```
var qldb = require('amazon-qldb-driver-nodejs');
var ionjs = require('ion-js');
```

------
#### [ TypeScript ]

```
import { QldbDriver, TransactionExecutor } from "amazon-qldb-driver-nodejs";
import { dom, dumpBinary, load } from "ion-js";
```

------

**Nota**  
Questo esempio importa anche il pacchetto Amazon Ion (`ion-js`). Questo pacchetto è necessario per elaborare i dati Ion durante l'esecuzione di alcune operazioni sui dati in questo riferimento. Per ulteriori informazioni, consulta [Lavorare con Amazon Ion](#cookbook-nodejs.ion).

## Istanziazione del driver


Il seguente esempio di codice crea un'istanza del driver che si connette a un nome di registro specificato utilizzando le impostazioni predefinite.

------
#### [ JavaScript ]

```
const qldbDriver = new qldb.QldbDriver("vehicle-registration");
```

------
#### [ TypeScript ]

```
const qldbDriver: QldbDriver = new QldbDriver("vehicle-registration");
```

------

## Operazioni CRUD


QLDB *esegue operazioni di creazione, lettura, aggiornamento ed* eliminazione (CRUD) come parte di una transazione.

**avvertimento**  
Come best practice, 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 in 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 conflitti ottimistici di controllo della concorrenza (OCC).*

[Modello di concorrenza Amazon QLDB](concurrency.md)Per ulteriori informazioni su OCC, vedere.

**Transazioni create implicitamente**

Il metodo [QldbDriver.executeLambda](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/classes/_src_qldbdriver_.qldbdriver.html#executelambda) accetta una funzione lambda che riceve un'istanza di [TransactionExecutor](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/classes/_src_transactionexecutor_.transactionexecutor.html), che può essere utilizzata per eseguire istruzioni. L'istanza di include una transazione creata `TransactionExecutor` implicitamente.

È possibile eseguire istruzioni all'interno della funzione lambda utilizzando il metodo [execute dell'esecutore](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/classes/_src_transactionexecutor_.transactionexecutor.html#execute) della transazione. Il driver esegue implicitamente la transazione quando ritorna la funzione lambda.

**Nota**  
Il `execute` metodo supporta sia i tipi Amazon Ion che i tipi nativi di Node.js. Se si passa un tipo nativo Node.js come argomento`execute`, il driver lo converte in un tipo Ion utilizzando il `ion-js` pacchetto (a condizione che sia supportata la conversione per il tipo di dati Node.js specificato). Per i tipi di dati e le regole di conversione supportati, consulta Ion JavaScript DOM [README](https://github.com/amzn/ion-js/blob/master/src/dom/README.md).

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

**Contents**
+ [

### Creazione di tabelle
](#cookbook-nodejs.crud.creating-tables)
+ [

### Creazione di indici
](#cookbook-nodejs.crud.creating-indexes)
+ [

### Leggere documenti
](#cookbook-nodejs.crud.reading)
  + [

#### Utilizzo dei parametri di interrogazione
](#cookbook-nodejs.reading-using-params)
+ [

### Inserimento di documenti
](#cookbook-nodejs.crud.inserting)
  + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-nodejs.crud.inserting.multiple)
+ [

### Aggiornamento dei documenti
](#cookbook-nodejs.crud.updating)
+ [

### Eliminazione di documenti
](#cookbook-nodejs.crud.deleting)
+ [

### Esecuzione di più istruzioni in una transazione
](#cookbook-nodejs.crud.multi-statement)
+ [

### Logica di ripetizione dei tentativi
](#cookbook-nodejs.crud.retry-logic)
+ [

### Implementazione di vincoli di unicità
](#cookbook-nodejs.crud.uniqueness-constraints)

### Creazione di tabelle


------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute("CREATE TABLE Person");
    });
})();
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('CREATE TABLE Person');
    });
}());
```

------

### Creazione di indici


------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute("CREATE INDEX ON Person (GovId)");
    });
})();
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('CREATE INDEX ON Person (GovId)');
    });
}());
```

------

### Leggere documenti


------
#### [ JavaScript ]

```
(async function() {
    // Assumes that Person table has documents as follows:
    // { "GovId": "TOYENC486FH", "FirstName": "Brent" }
    await qldbDriver.executeLambda(async (txn) => {
        const results = (await txn.execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'")).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    // Assumes that Person table has documents as follows:
    // { "GovId": "TOYENC486FH", "FirstName": "Brent" }
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const results: dom.Value[] = (await txn.execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'")).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------

#### Utilizzo dei parametri di interrogazione


Il seguente esempio di codice utilizza un parametro di query di tipo nativo.

------
#### [ JavaScript ]

```
(async function() {
    // Assumes that Person table has documents as follows:
    // { "GovId": "TOYENC486FH", "FirstName": "Brent" }
    await qldbDriver.executeLambda(async (txn) => {
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    // Assumes that Person table has documents as follows:
    // { "GovId": "TOYENC486FH", "FirstName": "Brent" }
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------

Il seguente esempio di codice utilizza un parametro di query di tipo Ion.

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const govId = ionjs.load("TOYENC486FH");

        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const govId: dom.Value = load("TOYENC486FH");

        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------

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

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ? AND FirstName = ?', 'TOYENC486FH', 'Brent')).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ? AND FirstName = ?', 'TOYENC486FH', 'Brent')).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------

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

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const govIds = ['TOYENC486FH','LOGANB486CG','LEWISR261LL'];
        /*
        Assumes that Person table has documents as follows:
        { "GovId": "TOYENC486FH", "FirstName": "Brent" }
        { "GovId": "LOGANB486CG", "FirstName": "Brent" }
        { "GovId": "LEWISR261LL", "FirstName": "Raul" }
        */
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId IN (?,?,?)', ...govIds)).getResultList();
        for (let result of results) {
            console.log(result.get('GovId'));
            console.log(result.get('FirstName'));
            /*
            prints:
            [String: 'TOYENC486FH']
            [String: 'Brent']
            [String: 'LOGANB486CG']
            [String: 'Brent']
            [String: 'LEWISR261LL']
            [String: 'Raul']
            */
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const govIds: string[] = ['TOYENC486FH','LOGANB486CG','LEWISR261LL'];
        /*
        Assumes that Person table has documents as follows:
        { "GovId": "TOYENC486FH", "FirstName": "Brent" }
        { "GovId": "LOGANB486CG", "FirstName": "Brent" }
        { "GovId": "LEWISR261LL", "FirstName": "Raul" }
        */
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId IN (?,?,?)', ...govIds)).getResultList();
        for (let result of results) {
            console.log(result.get('GovId'));
            console.log(result.get('FirstName'));
            /*
            prints:
            [String: 'TOYENC486FH']
            [String: 'Brent']
            [String: 'LOGANB486CG']
            [String: 'Brent']
            [String: 'LEWISR261LL']
            [String: 'Raul']
            */
        }
    });
}());
```

------

**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](ql-reference.create-index.md) sul campo per ottimizzare le `GovId` prestazioni. Senza un indice attivo`GovId`, le query possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

### Inserimento di documenti


Il seguente esempio di codice inserisce tipi di dati nativi.

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        // Check if doc with GovId:TOYENC486FH exists
        // This is critical to make this transaction idempotent
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            const doc = {
                'FirstName': 'Brent',
                'GovId': 'TOYENC486FH',
            };
            await txn.execute('INSERT INTO Person ?', doc);
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        // Check if doc with GovId:TOYENC486FH exists
        // This is critical to make this transaction idempotent
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            const doc: Record<string, string> = {
                'FirstName': 'Brent',
                'GovId': 'TOYENC486FH',
            };
            await txn.execute('INSERT INTO Person ?', doc);
        }
    });
}());
```

------

Il seguente esempio di codice inserisce i tipi di dati Ion.

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        // Check if doc with GovId:TOYENC486FH exists
        // This is critical to make this transaction idempotent
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            const doc = {
                'FirstName': 'Brent',
                'GovId': 'TOYENC486FH',
            };
            // Create a sample Ion doc
            const ionDoc = ionjs.load(ionjs.dumpBinary(doc));

            await txn.execute('INSERT INTO Person ?', ionDoc);
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        // Check if doc with GovId:TOYENC486FH exists
        // This is critical to make this transaction idempotent
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            const doc: Record<string, string> = {
                'FirstName': 'Brent',
                'GovId': 'TOYENC486FH',
            };
            // Create a sample Ion doc
            const ionDoc: dom.Value = load(dumpBinary(doc));

            await txn.execute('INSERT INTO Person ?', ionDoc);
        }
    });
}());
```

------

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 attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

#### Inserimento di più documenti in un'unica dichiarazione


Per inserire più documenti utilizzando una singola [INSERT](ql-reference.insert.md) istruzione, è possibile passare un parametro di tipo [elenco](driver-working-with-ion.md#driver-ion-list) all'istruzione come segue.

```
// people is a list
txn.execute("INSERT INTO People ?", people);
```

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

### Aggiornamento dei documenti


Il seguente esempio di codice utilizza tipi di dati nativi.

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', 'John', 'TOYENC486FH');
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', 'John', 'TOYENC486FH');
    });
}());
```

------

Il seguente esempio di codice utilizza i tipi di dati Ion.

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const firstName = ionjs.load("John");
        const govId = ionjs.load("TOYENC486FH");

        await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', firstName, govId);
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const firstName: dom.Value = load("John");
        const govId: dom.Value = load("TOYENC486FH");

        await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', firstName, govId);
    });
}());
```

------

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

### Eliminazione di documenti


Il seguente esempio di codice utilizza tipi di dati nativi.

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute('DELETE FROM Person WHERE GovId = ?', 'TOYENC486FH');
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('DELETE FROM Person WHERE GovId = ?', 'TOYENC486FH');
    });
}());
```

------

Il seguente esempio di codice utilizza i tipi di dati Ion.

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const govId = ionjs.load("TOYENC486FH");

        await txn.execute('DELETE FROM Person WHERE GovId = ?', govId);
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const govId: dom.Value = load("TOYENC486FH");

        await txn.execute('DELETE FROM Person WHERE GovId = ?', govId);
    });
}());
```

------

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

### Esecuzione di più istruzioni in una transazione


------
#### [ TypeScript ]

```
// 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.
async function insureCar(driver: QldbDriver, vin: string): Promise<boolean> {

    return await driver.executeLambda(async (txn: TransactionExecutor) => {
        const results: dom.Value[] = (await txn.execute(
            "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)).getResultList();

        if (results.length > 0) {
            await txn.execute(
                "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin);
            return true;
        }
        return false;
    });
};
```

------

### Logica di ripetizione dei tentativi


Il `executeLambda` 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). 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 è [defaultBackoffFunction](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/modules/_src_retry_defaultretryconfig_.html#defaultretryconfig)con una base di millisecondi. `10` È possibile impostare la configurazione dei nuovi tentativi per istanza del driver e anche per transazione utilizzando un'istanza di. [RetryConfig](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/classes/_src_retry_retryconfig_.retryconfig.html)

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.

------
#### [ JavaScript ]

```
var qldb = require('amazon-qldb-driver-nodejs');

// Configuring retry limit to 2
const retryConfig = new qldb.RetryConfig(2);
const qldbDriver = new qldb.QldbDriver("test-ledger", undefined, undefined, retryConfig);

// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff = (retryAttempt, error, transactionId) => {
    return 1000 * retryAttempt;
};

const retryConfigCustomBackoff = new qldb.RetryConfig(2, customBackoff);
const qldbDriverCustomBackoff = new qldb.QldbDriver("test-ledger", undefined, undefined, retryConfigCustomBackoff);
```

------
#### [ TypeScript ]

```
import { BackoffFunction, QldbDriver, RetryConfig } from "amazon-qldb-driver-nodejs"

// Configuring retry limit to 2
const retryConfig: RetryConfig = new RetryConfig(2);
const qldbDriver: QldbDriver = new QldbDriver("test-ledger", undefined, undefined, retryConfig);

// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff: BackoffFunction = (retryAttempt: number, error: Error, transactionId: string) => {
    return 1000 * retryAttempt;
};

const retryConfigCustomBackoff: RetryConfig = new RetryConfig(2, customBackoff);
const qldbDriverCustomBackoff: QldbDriver = new QldbDriver("test-ledger", undefined, undefined, retryConfigCustomBackoff);
```

------

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 esecuzione lambda. Questa configurazione `executeLambda` sostituisce la logica di riprova impostata per l'istanza del driver.

------
#### [ JavaScript ]

```
var qldb = require('amazon-qldb-driver-nodejs');

// Configuring retry limit to 2
const retryConfig1 = new qldb.RetryConfig(2);
const qldbDriver = new qldb.QldbDriver("test-ledger", undefined, undefined, retryConfig1);

// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff = (retryAttempt, error, transactionId) => {
    return 1000 * retryAttempt;
};

const retryConfig2 = new qldb.RetryConfig(2, customBackoff);

// The config `retryConfig1` will be overridden by `retryConfig2`
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute('CREATE TABLE Person');
    }, retryConfig2);
}());
```

------
#### [ TypeScript ]

```
import { BackoffFunction, QldbDriver, RetryConfig, TransactionExecutor } from "amazon-qldb-driver-nodejs"

// Configuring retry limit to 2
const retryConfig1: RetryConfig = new RetryConfig(2);
const qldbDriver: QldbDriver = new QldbDriver("test-ledger", undefined, undefined, retryConfig1);

// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff: BackoffFunction = (retryAttempt: number, error: Error, transactionId: string) => {
    return 1000 * retryAttempt;
};

const retryConfig2: RetryConfig = new RetryConfig(2, customBackoff);

// The config `retryConfig1` will be overridden by `retryConfig2`
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('CREATE TABLE Person');
    }, retryConfig2);
}());
```

------

### Implementazione di vincoli di unicità


QLDB non 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 specificato`GovId`.

1. 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'eccezione relativa al conflitto OCC.

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

------
#### [ JavaScript ]

```
const govId = 'TOYENC486FH';
const document = {
    'FirstName': 'Brent',
    'GovId': 'TOYENC486FH',
};
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        // Check if doc with GovId = govId exists
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            await txn.execute('INSERT INTO Person ?', document);
        }
    });
})();
```

------
#### [ TypeScript ]

```
const govId: string = 'TOYENC486FH';
const document: Record<string, string> = {
    'FirstName': 'Brent',
    'GovId': 'TOYENC486FH',
};
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        // Check if doc with GovId = govId exists
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            await txn.execute('INSERT INTO Person ?', document);
        }
    });
})();
```

------

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

## Lavorare con Amazon Ion


Le seguenti sezioni mostrano come utilizzare il modulo Amazon Ion per elaborare i dati Ion.

**Contents**
+ [

### Importazione del modulo Ion
](#cookbook-nodejs.ion.import)
+ [

### Creazione di tipi di ioni
](#cookbook-nodejs.ion.creating-types)
+ [

### Ottenere un dump binario Ion
](#cookbook-nodejs.ion.getting-binary)
+ [

### Ottenere un dump di testo Ion
](#cookbook-nodejs.ion.getting-text)

### Importazione del modulo Ion


------
#### [ JavaScript ]

```
var ionjs = require('ion-js');
```

------
#### [ TypeScript ]

```
import { dom, dumpBinary, dumpText, load } from "ion-js";
```

------

### Creazione di tipi di ioni


Il seguente esempio di codice crea un oggetto Ion dal testo Ion.

------
#### [ JavaScript ]

```
const ionText  = '{GovId: "TOYENC486FH", FirstName: "Brent"}';
const ionObj = ionjs.load(ionText);

console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
```

------
#### [ TypeScript ]

```
const ionText: string = '{GovId: "TOYENC486FH", FirstName: "Brent"}';
const ionObj: dom.Value = load(ionText);

console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
```

------

Il seguente esempio di codice crea un oggetto Ion da un dizionario Node.js.

------
#### [ JavaScript ]

```
const aDict = {
    'GovId': 'TOYENC486FH',
    'FirstName': 'Brent'
};
const ionObj = ionjs.load(ionjs.dumpBinary(aDict));
console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
```

------
#### [ TypeScript ]

```
const aDict: Record<string, string> = {
    'GovId': 'TOYENC486FH',
    'FirstName': 'Brent'
};
const ionObj: dom.Value = load(dumpBinary(aDict));
console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
```

------

### Ottenere un dump binario Ion


------
#### [ JavaScript ]

```
// ionObj is an Ion struct
console.log(ionjs.dumpBinary(ionObj).toString()); // prints 224,1,0,234,238,151,129,131,222,147,135,190,144,133,71,111,118,73,100,137,70,105,114,115,116,78,97,109,101,222,148,138,139,84,79,89,69,78,67,52,56,54,70,72,139,133,66,114,101,110,116
```

------
#### [ TypeScript ]

```
// ionObj is an Ion struct
console.log(dumpBinary(ionObj).toString()); // prints 224,1,0,234,238,151,129,131,222,147,135,190,144,133,71,111,118,73,100,137,70,105,114,115,116,78,97,109,101,222,148,138,139,84,79,89,69,78,67,52,56,54,70,72,139,133,66,114,101,110,116
```

------

### Ottenere un dump di testo Ion


------
#### [ JavaScript ]

```
// ionObj is an Ion struct
console.log(ionjs.dumpText(ionObj)); // prints {GovId:"TOYENC486FH",FirstName:"Brent"}
```

------
#### [ TypeScript ]

```
// ionObj is an Ion struct
console.log(dumpText(ionObj)); // prints {GovId:"TOYENC486FH",FirstName:"Brent"}
```

------

Per ulteriori informazioni su Ion, consulta la [documentazione di Amazon Ion](http://amzn.github.io/ion-docs/) su GitHub. Per altri esempi di codice sull'utilizzo di Ion in QLDB, vedere. [Utilizzo dei tipi di dati Amazon Ion in Amazon QLDB](driver-working-with-ion.md)

# Driver Amazon QLDB per Python
Driver Python

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Per lavorare con i dati nel tuo registro, puoi connetterti ad Amazon QLDB dall'applicazione Python utilizzando un driver fornito. AWS I seguenti argomenti descrivono come iniziare a usare il driver QLDB per Python.

**Topics**
+ [

## Risorse per i driver
](#getting-started.python.resources)
+ [

## Prerequisiti
](#getting-started.python.prereqs)
+ [

## Installazione
](#getting-started.python.install)
+ [Tutorial di avvio rapido](driver-quickstart-python.md)
+ [Riferimento al libro di cucina](driver-cookbook-python.md)

## Risorse per i driver


Per ulteriori informazioni sulle funzionalità supportate dal driver Python, consultate le seguenti risorse:
+ [Riferimento API: [3.x, 2.x](https://amazon-qldb-driver-python.readthedocs.io/en/latest/)](https://amazon-qldb-driver-python.readthedocs.io/en/v2.0.2/)
+ [Codice sorgente del driver () GitHub](https://github.com/awslabs/amazon-qldb-driver-python)
+ [Codice sorgente dell'applicazione di esempio (GitHub)](https://github.com/aws-samples/amazon-qldb-dmv-sample-python)
+ [Esempi di codice Amazon Ion](ion.code-examples.md)

## Prerequisiti


Prima di iniziare con il driver QLDB per Python, devi fare quanto segue:

1. Segui le istruzioni di AWS configurazione riportate in. [Accesso ad Amazon QLDB](accessing.md) Questo include gli output seguenti:

   1. Iscriviti a AWS.

   1. Crea un utente con le autorizzazioni QLDB appropriate.

   1. Concedi l'accesso programmatico per lo sviluppo.

1. Installa una delle seguenti versioni di Python dal sito di download di [Python](https://www.python.org/downloads/):
   + **3.6 o successivo — driver** QLDB per Python v3
   + **3.4 o successivo** — driver QLDB per Python v2

1. Imposta le tue AWS credenziali e quelle predefinite. Regione AWS Per istruzioni, consulta [Quickstart](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html#configuration) nella AWS SDK per Python (Boto3) documentazione.

   Per un elenco completo delle regioni disponibili, consulta gli [endpoint e le quote di Amazon QLDB](https://docs.aws.amazon.com/general/latest/gr/qldb.html) nel. *Riferimenti generali di AWS*

Successivamente, puoi scaricare l'applicazione di esempio completa del tutorial oppure puoi installare solo il driver in un progetto Python ed eseguire brevi esempi di codice.
+ Per installare il driver QLDB e AWS SDK per Python (Boto3) poi in un progetto esistente, procedi con. [Installazione](#getting-started.python.install)
+ Per configurare un progetto ed eseguire brevi esempi di codice che illustrano le transazioni di dati di base su un registro, consulta il. [Tutorial di avvio rapido](driver-quickstart-python.md)
+ Per eseguire esempi più approfonditi delle operazioni delle API relative ai dati e alla gestione nell'applicazione di esempio completa del tutorial, consulta il. [Tutorial di Python](getting-started.python.tutorial.md)

## Installazione


QLDB supporta le seguenti versioni dei driver e le relative dipendenze in Python.


****  

| Versione driver | Versione di Python | Stato | Data di rilascio più recente | 
| --- | --- | --- | --- | 
| [2.x](https://pypi.org/project/pyqldb/2.0.2/) | 3.4 o versione successiva | Versione di produzione | 7 maggio 2020 | 
| [3.x](https://pypi.org/project/pyqldb/) | 3.6 o versione successiva | Rilascio di produzione | 28 ottobre 2021 | 

Per installare il driver QLDB da PyPI `pip` utilizzando (un gestore di pacchetti per Python), inserisci quanto segue nella riga di comando.

------
#### [ 3.x ]

```
pip install pyqldb
```

------
#### [ 2.x ]

```
pip install pyqldb==2.0.2
```

------

L'installazione del driver installa anche le sue dipendenze, inclusi i pacchetti Amazon [Ion e [AWS SDK per Python (Boto3)](https://aws.amazon.com/sdk-for-python)Amazon.](ion.md)

**Utilizzo del driver per connettersi a un registro**

Quindi puoi importare il driver e usarlo per connetterti a un libro mastro. Il seguente esempio di codice Python mostra come creare una sessione per un nome di registro specificato.

------
#### [ 3.x ]

```
from pyqldb.driver.qldb_driver import QldbDriver
qldb_driver = QldbDriver(ledger_name='testLedger')

for table in qldb_driver.list_tables():
    print(table)
```

------
#### [ 2.x ]

```
from pyqldb.driver.pooled_qldb_driver import PooledQldbDriver

qldb_driver = PooledQldbDriver(ledger_name='testLedger')
qldb_session = qldb_driver.get_session()

for table in qldb_session.list_tables():
    print(table)
```

------

Per brevi esempi di codice su come eseguire transazioni di dati di base su un registro, consulta il. [Riferimento al libro di cucina](driver-cookbook-python.md)

# Driver Amazon QLDB per Python — Tutorial di avvio rapido
Tutorial di avvio rapido

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo tutorial, imparerai come configurare una semplice applicazione utilizzando l'ultima versione del driver Amazon QLDB per Python. Questa guida include i passaggi per l'installazione del driver e brevi esempi in codice delle operazioni di base di *creazione, lettura, aggiornamento ed eliminazione* (CRUD). Per esempi più approfonditi che illustrano queste operazioni in un'applicazione di esempio completa, consulta la. [Tutorial di Python](getting-started.python.tutorial.md)

**Topics**
+ [

## Prerequisiti
](#driver-quickstart-python.prereqs)
+ [

## Fase 1: Configurazione del progetto
](#driver-quickstart-python.step-1)
+ [

## Fase 2: Inizializzare il driver
](#driver-quickstart-python.step-2)
+ [

## Fase 3: Creare una tabella e un indice
](#driver-quickstart-python.step-3)
+ [

## Fase 4: Inserimento di un documento
](#driver-quickstart-python.step-4)
+ [

## Fase 5: Interrogare il documento
](#driver-quickstart-python.step-5)
+ [

## Fase 6: Aggiornare il documento
](#driver-quickstart-python.step-6)
+ [

## Esecuzione dell'applicazione completa
](#driver-quickstart-python.complete)

## Prerequisiti


Prima di iniziare, assicurati di fare quanto segue:

1. Completa il driver [Prerequisiti](getting-started.python.md#getting-started.python.prereqs) for the Python, se non l'hai già fatto. Ciò include la registrazione AWS, la concessione dell'accesso programmatico per lo sviluppo e l'installazione di Python versione 3.6 o successiva.

1. Crea un libro mastro denominato. `quick-start`

   Per informazioni su come creare un libro mastro, consulta [Operazioni di base per i registri Amazon QLDB](ledger-management.basics.md) o [Fase 1: Creare un nuovo libro contabile](getting-started-step-1.md) in *Guida introduttiva alla console*.

## Fase 1: Configurazione del progetto


Per prima cosa, configura il tuo progetto Python.

**Nota**  
Se utilizzi un IDE con funzionalità per automatizzare questi passaggi di configurazione, puoi passare direttamente a. [Fase 2: Inizializzare il driver](#driver-quickstart-python.step-2)

1. Crea una cartella per la tua applicazione.

   ```
   $ mkdir myproject
   $ cd myproject
   ```

1. Per installare il driver QLDB per Python da PyPI, inserisci il seguente comando. `pip`

   ```
   $ pip install pyqldb
   ```

   L'installazione del driver installa anche le sue dipendenze, inclusi i pacchetti Amazon [Ion e [AWS SDK per Python (Boto3)](https://aws.amazon.com/sdk-for-net)Amazon.](ion.md)

1. Crea un nuovo file denominato `app.py`.

   Quindi, aggiungi in modo incrementale gli esempi di codice nei passaggi seguenti per provare alcune operazioni CRUD di base. [In alternativa, puoi saltare il step-by-step tutorial ed eseguire invece l'applicazione completa.](#driver-quickstart-python.complete)

## Fase 2: Inizializzare il driver


Inizializza un'istanza del driver che si connette al registro denominato. `quick-start` Aggiungi il codice seguente al tuo `app.py` file.

```
from pyqldb.config.retry_config import RetryConfig
from pyqldb.driver.qldb_driver import QldbDriver

# Configure retry limit to 3
retry_config = RetryConfig(retry_limit=3)

# Initialize the driver
print("Initializing the driver")
qldb_driver = QldbDriver("quick-start", retry_config=retry_config)
```

## Fase 3: Creare una tabella e un indice


Il seguente esempio di codice mostra come eseguire le `CREATE INDEX` istruzioni `CREATE TABLE` e le istruzioni.

Aggiungere il codice seguente che crea una tabella denominata `People` e un indice per il `lastName` campo di tale tabella. [Gli indici](ql-reference.create-index.md) sono necessari per ottimizzare le prestazioni delle query e aiutano a limitare le eccezioni ai conflitti [OCC (Optimistic Concurrency Control)](concurrency.md).

```
def create_table(transaction_executor):
    print("Creating a table")
    transaction_executor.execute_statement("Create TABLE People")

def create_index(transaction_executor):
    print("Creating an index")
    transaction_executor.execute_statement("CREATE INDEX ON People(lastName)")

# Create a table
qldb_driver.execute_lambda(lambda executor: create_table(executor))

# Create an index on the table
qldb_driver.execute_lambda(lambda executor: create_index(executor))
```

## Fase 4: Inserimento di un documento


Il seguente esempio di codice mostra come eseguire un'`INSERT`istruzione. QLDB supporta il linguaggio di interrogazione [PartiQL](ql-reference.md) (compatibile con SQL) e il formato dati [Amazon](ion.md) Ion (superset di JSON).

Aggiungi il codice seguente che inserisce un documento nella tabella. `People`

```
def insert_documents(transaction_executor, arg_1):
    print("Inserting a document")
    transaction_executor.execute_statement("INSERT INTO People ?", arg_1)

# Insert a document
doc_1 = { 'firstName': "John",
          'lastName': "Doe",
          'age': 32,
        }

qldb_driver.execute_lambda(lambda x: insert_documents(x, doc_1))
```

Questo esempio utilizza un punto interrogativo (`?`) come segnaposto variabile per passare le informazioni del documento all'istruzione. Il `execute_statement` metodo supporta valori sia nei tipi Amazon Ion che nei tipi nativi Python.

**Suggerimento**  
Per inserire più documenti utilizzando una singola [INSERT](ql-reference.insert.md) istruzione, puoi passare un parametro di tipo [elenco](driver-working-with-ion.md#driver-ion-list) all'istruzione come segue.  

```
# people is a list
transaction_executor.execute_statement("INSERT INTO Person ?", people)
```
Non racchiudete la variabile placeholder (`?`) tra parentesi angolari doppie (`<<...>>`) quando passate un elenco. *Nelle istruzioni PartiQL manuali, le parentesi doppie angolari indicano una raccolta non ordinata nota come borsa.*

## Fase 5: Interrogare il documento


Il seguente esempio di codice mostra come eseguire un'`SELECT`istruzione.

Aggiungere il codice seguente per interrogare un documento dalla `People` tabella.

```
def read_documents(transaction_executor):
    print("Querying the table")
    cursor = transaction_executor.execute_statement("SELECT * FROM People WHERE lastName = ?", 'Doe')

    for doc in cursor:
        print(doc["firstName"])
        print(doc["lastName"])
        print(doc["age"])

# Query the table
qldb_driver.execute_lambda(lambda executor: read_documents(executor))
```

## Fase 6: Aggiornare il documento


Il seguente esempio di codice mostra come eseguire un'`UPDATE`istruzione.

1. Aggiungere il codice seguente che aggiorna un documento nella `People` tabella aggiornandolo `age` a`42`.

   ```
   def update_documents(transaction_executor, age, lastName):
       print("Updating the document")
       transaction_executor.execute_statement("UPDATE People SET age = ? WHERE lastName = ?", age, lastName)
   
   # Update the document
   age = 42
   lastName = 'Doe'
   
   qldb_driver.execute_lambda(lambda x: update_documents(x, age, lastName))
   ```

1. Eseguite nuovamente una query sulla tabella per vedere il valore aggiornato.

   ```
   # Query the updated document
   qldb_driver.execute_lambda(lambda executor: read_documents(executor))
   ```

1. Per eseguire l'applicazione, immettete il seguente comando dalla directory del progetto.

   ```
   $ python app.py
   ```

## Esecuzione dell'applicazione completa


Il seguente esempio di codice è la versione completa dell'`app.py`applicazione. Invece di eseguire i passaggi precedenti singolarmente, potete anche copiare ed eseguire questo esempio di codice dall'inizio alla fine. Questa applicazione dimostra alcune operazioni CRUD di base sul registro denominato. `quick-start`

**Nota**  
Prima di eseguire questo codice, assicuratevi di non avere già una tabella attiva denominata `People` nel registro. `quick-start`

```
from pyqldb.config.retry_config import RetryConfig
from pyqldb.driver.qldb_driver import QldbDriver

def create_table(transaction_executor):
    print("Creating a table")
    transaction_executor.execute_statement("CREATE TABLE People")

def create_index(transaction_executor):
    print("Creating an index")
    transaction_executor.execute_statement("CREATE INDEX ON People(lastName)")

def insert_documents(transaction_executor, arg_1):
    print("Inserting a document")
    transaction_executor.execute_statement("INSERT INTO People ?", arg_1)

def read_documents(transaction_executor):
    print("Querying the table")
    cursor = transaction_executor.execute_statement("SELECT * FROM People WHERE lastName = ?", 'Doe')
                                                                                                                                          
    for doc in cursor:
        print(doc["firstName"])
        print(doc["lastName"])
        print(doc["age"])

def update_documents(transaction_executor, age, lastName):
    print("Updating the document")
    transaction_executor.execute_statement("UPDATE People SET age = ? WHERE lastName = ?", age, lastName)

# Configure retry limit to 3
retry_config = RetryConfig(retry_limit=3)

# Initialize the driver
print("Initializing the driver")
qldb_driver = QldbDriver("quick-start", retry_config=retry_config)

# Create a table
qldb_driver.execute_lambda(lambda executor: create_table(executor))

# Create an index on the table
qldb_driver.execute_lambda(lambda executor: create_index(executor))

# Insert a document
doc_1 = { 'firstName': "John",
          'lastName': "Doe",
          'age': 32,
        }

qldb_driver.execute_lambda(lambda x: insert_documents(x, doc_1))

# Query the table
qldb_driver.execute_lambda(lambda executor: read_documents(executor))

# Update the document
age = 42
lastName = 'Doe'

qldb_driver.execute_lambda(lambda x: update_documents(x, age, lastName))

# Query the table for the updated document
qldb_driver.execute_lambda(lambda executor: read_documents(executor))
```

Per eseguire l'applicazione completa, immettete il seguente comando dalla directory del progetto.

```
$ python app.py
```

# Driver Amazon QLDB per Python — Guida di riferimento al libro di cucina
Riferimento al libro di cucina

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questa guida di riferimento mostra i casi d'uso comuni del driver Amazon QLDB per Python. Fornisce esempi di codice Python 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 QLDB per Python.

**Contents**
+ [

## Importazione del driver
](#cookbook-python.importing)
+ [

## Istanziazione del driver
](#cookbook-python.instantiating)
+ [

## Operazioni CRUD
](#cookbook-python.crud)
  + [

### Creazione di tabelle
](#cookbook-python.crud.creating-tables)
  + [

### Creazione di indici
](#cookbook-python.crud.creating-indexes)
  + [

### Leggere documenti
](#cookbook-python.crud.reading)
    + [

#### Utilizzo dei parametri di interrogazione
](#cookbook-python.reading-using-params)
  + [

### Inserimento di documenti
](#cookbook-python.crud.inserting)
    + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-python.crud.inserting.multiple)
  + [

### Aggiornamento dei documenti
](#cookbook-python.crud.updating)
  + [

### Eliminazione di documenti
](#cookbook-python.crud.deleting)
  + [

### Esecuzione di più istruzioni in una transazione
](#cookbook-python.crud.multi-statement)
  + [

### Logica di ripetizione dei tentativi
](#cookbook-python.crud.retry-logic)
  + [

### Implementazione di vincoli di unicità
](#cookbook-python.crud.uniqueness-constraints)
+ [

## Lavorare con Amazon Ion
](#cookbook-python.ion)
  + [

### Importazione del modulo Ion
](#cookbook-python.ion.import)
  + [

### Creazione di tipi di ioni
](#cookbook-python.ion.creating-types)
  + [

### Ottenere un dump binario Ion
](#cookbook-python.ion.getting-binary)
  + [

### Ottenere un dump di testo Ion
](#cookbook-python.ion.getting-text)

## Importazione del driver


Il seguente esempio di codice importa il driver.

------
#### [ 3.x ]

```
from pyqldb.driver.qldb_driver import QldbDriver
import amazon.ion.simpleion as simpleion
```

------
#### [ 2.x ]

```
from pyqldb.driver.pooled_qldb_driver import PooledQldbDriver
import amazon.ion.simpleion as simpleion
```

------

**Nota**  
Questo esempio importa anche il pacchetto Amazon Ion (`amazon.ion.simpleion`). Questo pacchetto è necessario per elaborare i dati Ion durante l'esecuzione di alcune operazioni sui dati in questo riferimento. Per ulteriori informazioni, consulta [Lavorare con Amazon Ion](#cookbook-python.ion).

## Istanziazione del driver


Il seguente esempio di codice crea un'istanza del driver che si connette a un nome di registro specificato utilizzando le impostazioni predefinite.

------
#### [ 3.x ]

```
qldb_driver = QldbDriver(ledger_name='vehicle-registration')
```

------
#### [ 2.x ]

```
qldb_driver = PooledQldbDriver(ledger_name='vehicle-registration')
```

------

## Operazioni CRUD


QLDB *esegue operazioni di creazione, lettura, aggiornamento ed* eliminazione (CRUD) come parte di una transazione.

**avvertimento**  
Come best practice, 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 in 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 conflitti ottimistici di controllo della concorrenza (OCC).*

[Modello di concorrenza Amazon QLDB](concurrency.md)Per ulteriori informazioni su OCC, vedere.

**Transazioni create implicitamente**

[Il metodo [pyqldb.driver.qldb\$1driver.execute\$1lambda accetta una funzione lambda che riceve un'istanza di pyQLDB.Execution.Executor.Executor](https://amazon-qldb-driver-python.readthedocs.io/en/stable/reference/driver/qldb_driver.html#pyqldb.driver.qldb_driver.QldbDriver.execute_lambda), che puoi usare per eseguire istruzioni.](https://amazon-qldb-driver-python.readthedocs.io/en/stable/reference/execution/executor.html#pyqldb.execution.executor.Executor) L'istanza di include una `Executor` transazione creata implicitamente.

È possibile eseguire istruzioni all'interno della funzione lambda utilizzando il metodo [execute\$1statement dell'esecutore](https://amazon-qldb-driver-python.readthedocs.io/en/stable/reference/execution/executor.html#pyqldb.execution.executor.Executor.execute_statement) della transazione. Il driver esegue implicitamente la transazione quando ritorna la funzione lambda.

**Nota**  
Il `execute_statement` metodo supporta sia i tipi Amazon Ion che i tipi nativi di Python. Se si passa un tipo nativo Python come argomento a`execute_statement`, il driver lo converte in un tipo Ion utilizzando il `amazon.ion.simpleion` modulo (a condizione che sia supportata la conversione per il tipo di dati Python specificato). Per i tipi di dati e le regole di conversione supportati, consultate il codice sorgente di [simpleion](https://ion-python.readthedocs.io/en/latest/_modules/amazon/ion/simpleion.html).

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

**Contents**
+ [

### Creazione di tabelle
](#cookbook-python.crud.creating-tables)
+ [

### Creazione di indici
](#cookbook-python.crud.creating-indexes)
+ [

### Leggere documenti
](#cookbook-python.crud.reading)
  + [

#### Utilizzo dei parametri di interrogazione
](#cookbook-python.reading-using-params)
+ [

### Inserimento di documenti
](#cookbook-python.crud.inserting)
  + [

#### Inserimento di più documenti in un'unica dichiarazione
](#cookbook-python.crud.inserting.multiple)
+ [

### Aggiornamento dei documenti
](#cookbook-python.crud.updating)
+ [

### Eliminazione di documenti
](#cookbook-python.crud.deleting)
+ [

### Esecuzione di più istruzioni in una transazione
](#cookbook-python.crud.multi-statement)
+ [

### Logica di ripetizione dei tentativi
](#cookbook-python.crud.retry-logic)
+ [

### Implementazione di vincoli di unicità
](#cookbook-python.crud.uniqueness-constraints)

### Creazione di tabelle


```
def create_table(transaction_executor):
    transaction_executor.execute_statement("CREATE TABLE Person")

qldb_driver.execute_lambda(lambda executor: create_table(executor))
```

### Creazione di indici


```
def create_index(transaction_executor):
    transaction_executor.execute_statement("CREATE INDEX ON Person(GovId)")

qldb_driver.execute_lambda(lambda executor: create_index(executor))
```

### Leggere documenti


```
# Assumes that Person table has documents as follows:
# { "GovId": "TOYENC486FH", "FirstName": "Brent" }

def read_documents(transaction_executor):
    cursor = transaction_executor.execute_statement("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'")

    for doc in cursor:
        print(doc["GovId"]) # prints TOYENC486FH
        print(doc["FirstName"]) # prints Brent

qldb_driver.execute_lambda(lambda executor: read_documents(executor))
```

#### Utilizzo dei parametri di interrogazione


Il seguente esempio di codice utilizza un parametro di query di tipo nativo.

```
cursor = transaction_executor.execute_statement("SELECT * FROM Person WHERE GovId = ?", 'TOYENC486FH')
```

Il seguente esempio di codice utilizza un parametro di query di tipo Ion.

```
name = ion.loads('Brent')
cursor = transaction_executor.execute_statement("SELECT * FROM Person WHERE FirstName = ?", name)
```

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

```
cursor = transaction_executor.execute_statement("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", 'TOYENC486FH', "Brent")
```

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

```
gov_ids = ['TOYENC486FH','ROEE1','YH844']
cursor = transaction_executor.execute_statement("SELECT * FROM Person WHERE GovId IN (?,?,?)", *gov_ids)
```

**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](ql-reference.create-index.md) sul campo per ottimizzare le `GovId` prestazioni. Senza un indice attivo`GovId`, le query possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

### Inserimento di documenti


Il seguente esempio di codice inserisce tipi di dati nativi.

```
def insert_documents(transaction_executor, arg_1):
    # Check if doc with GovId:TOYENC486FH exists
    # This is critical to make this transaction idempotent
    cursor = transaction_executor.execute_statement("SELECT * FROM Person WHERE GovId = ?", 'TOYENC486FH')
    # Check if there is any record in the cursor
    first_record = next(cursor, None)

    if first_record:
        # Record already exists, no need to insert
        pass
    else:
        transaction_executor.execute_statement("INSERT INTO Person ?", arg_1)

doc_1 = { 'FirstName': "Brent",
          'GovId': 'TOYENC486FH',
        }

qldb_driver.execute_lambda(lambda executor: insert_documents(executor, doc_1))
```

Il seguente esempio di codice inserisce i tipi di dati Ion.

```
def insert_documents(transaction_executor, arg_1):
    # Check if doc with GovId:TOYENC486FH exists
    # This is critical to make this transaction idempotent
    cursor = transaction_executor.execute_statement("SELECT * FROM Person WHERE GovId = ?", 'TOYENC486FH')
    # Check if there is any record in the cursor
    first_record = next(cursor, None)

    if first_record:
        # Record already exists, no need to insert
        pass
    else:
        transaction_executor.execute_statement("INSERT INTO Person ?", arg_1)

doc_1 = { 'FirstName': 'Brent',
          'GovId': 'TOYENC486FH',
        }

# create a sample Ion doc
ion_doc_1 = simpleion.loads(simpleion.dumps(doc_1)))

qldb_driver.execute_lambda(lambda executor: insert_documents(executor, ion_doc_1))
```

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 attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC o a timeout delle transazioni.

#### Inserimento di più documenti in un'unica dichiarazione


Per inserire più documenti utilizzando una singola [INSERT](ql-reference.insert.md) istruzione, è possibile passare un parametro di tipo [elenco](driver-working-with-ion.md#driver-ion-list) all'istruzione come segue.

```
# people is a list
transaction_executor.execute_statement("INSERT INTO Person ?", people)
```

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

### Aggiornamento dei documenti


Il seguente esempio di codice utilizza tipi di dati nativi.

```
def update_documents(transaction_executor, gov_id, name):
    transaction_executor.execute_statement("UPDATE Person SET FirstName = ?  WHERE GovId = ?", name, gov_id)

gov_id = 'TOYENC486FH'
name = 'John'

qldb_driver.execute_lambda(lambda executor: update_documents(executor, gov_id, name))
```

Il seguente esempio di codice utilizza i tipi di dati Ion.

```
def update_documents(transaction_executor, gov_id, name):
    transaction_executor.execute_statement("UPDATE Person SET FirstName = ? WHERE GovId = ?", name, gov_id)

# Ion datatypes
gov_id = simpleion.loads('TOYENC486FH')
name = simpleion.loads('John')

qldb_driver.execute_lambda(lambda executor: update_documents(executor, gov_id, name))
```

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

### Eliminazione di documenti


Il seguente esempio di codice utilizza tipi di dati nativi.

```
def delete_documents(transaction_executor, gov_id):
    cursor = transaction_executor.execute_statement("DELETE FROM Person WHERE GovId = ?", gov_id)

gov_id = 'TOYENC486FH'

qldb_driver.execute_lambda(lambda executor: delete_documents(executor, gov_id))
```

Il seguente esempio di codice utilizza i tipi di dati Ion.

```
def delete_documents(transaction_executor, gov_id):
    cursor = transaction_executor.execute_statement("DELETE FROM Person WHERE GovId = ?", gov_id)

# Ion datatypes
gov_id = simpleion.loads('TOYENC486FH')

qldb_driver.execute_lambda(lambda executor: delete_documents(executor, gov_id))
```

**Nota**  
In questo esempio, si consiglia di disporre di un indice sul `GovId` campo per ottimizzare le prestazioni. Senza un indice attivo`GovId`, le istruzioni possono avere una maggiore latenza e possono anche portare a eccezioni nei conflitti OCC 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.

def do_insure_car(transaction_executor, vin):
    cursor = transaction_executor.execute_statement(
        "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)
    first_record = next(cursor, None)
    if first_record:
        transaction_executor.execute_statement(
            "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin)
        return True
    else:
        return False

def insure_car(qldb_driver, vin_to_insure):
    return qldb_driver.execute_lambda(
        lambda executor: do_insure_car(executor, vin_to_insure))
```

### Logica di ripetizione dei tentativi


Il `execute_lambda` 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).

------
#### [ 3.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 è [Exponential Backoff](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) and Jitter con una base di millisecondi. `10` [È possibile impostare la configurazione dei nuovi tentativi per istanza del driver e anche per transazione utilizzando un'istanza di pyqldb.config.retry\$1config. RetryConfig](https://amazon-qldb-driver-python.readthedocs.io/en/stable/reference/config/retry_config.html#pyqldb.config.retry_config.RetryConfig).

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

```
from pyqldb.config.retry_config import RetryConfig
from pyqldb.driver.qldb_driver import QldbDriver

# Configuring retry limit to 2
retry_config = RetryConfig(retry_limit=2)
qldb_driver = QldbDriver("test-ledger", retry_config=retry_config)

# Configuring a custom backoff which increases delay by 1s for each attempt.
def custom_backoff(retry_attempt, error, transaction_id):
    return 1000 * retry_attempt

retry_config_custom_backoff = RetryConfig(retry_limit=2, custom_backoff=custom_backoff)
qldb_driver = QldbDriver("test-ledger", retry_config=retry_config_custom_backoff)
```

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 esecuzione lambda. Questa configurazione `execute_lambda` sostituisce la logica di riprova impostata per l'istanza del driver.

```
from pyqldb.config.retry_config import RetryConfig
from pyqldb.driver.qldb_driver import QldbDriver

# Configuring retry limit to 2
retry_config_1 = RetryConfig(retry_limit=4)
qldb_driver = QldbDriver("test-ledger", retry_config=retry_config_1)

# Configuring a custom backoff which increases delay by 1s for each attempt.
def custom_backoff(retry_attempt, error, transaction_id):
    return 1000 * retry_attempt

retry_config_2 = RetryConfig(retry_limit=2, custom_backoff=custom_backoff)

# The config `retry_config_1` will be overriden by `retry_config_2`
qldb_driver.execute_lambda(lambda txn: txn.execute_statement("CREATE TABLE Person"), retry_config_2)
```

------
#### [ 2.x ]

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

Il limite di tentativi predefinito è. `4`

------

### Implementazione di vincoli di unicità


QLDB non 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 specificato`GovId`.

1. 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'eccezione relativa al conflitto OCC.

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

```
def insert_documents(transaction_executor, gov_id, document):
    # Check if doc with GovId = gov_id exists
    cursor = transaction_executor.execute_statement("SELECT * FROM Person WHERE GovId = ?", gov_id)
    # Check if there is any record in the cursor
    first_record = next(cursor, None)

    if first_record:
        # Record already exists, no need to insert
        pass
    else:
        transaction_executor.execute_statement("INSERT INTO Person ?", document)

qldb_driver.execute_lambda(lambda executor: insert_documents(executor, gov_id, document))
```

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

## Lavorare con Amazon Ion


Le seguenti sezioni mostrano come utilizzare il modulo Amazon Ion per elaborare i dati Ion.

**Contents**
+ [

### Importazione del modulo Ion
](#cookbook-python.ion.import)
+ [

### Creazione di tipi di ioni
](#cookbook-python.ion.creating-types)
+ [

### Ottenere un dump binario Ion
](#cookbook-python.ion.getting-binary)
+ [

### Ottenere un dump di testo Ion
](#cookbook-python.ion.getting-text)

### Importazione del modulo Ion


```
import amazon.ion.simpleion as simpleion
```

### Creazione di tipi di ioni


Il seguente esempio di codice crea un oggetto Ion dal testo Ion.

```
ion_text = '{GovId: "TOYENC486FH", FirstName: "Brent"}'
ion_obj = simpleion.loads(ion_text)

print(ion_obj['GovId']) # prints TOYENC486FH
print(ion_obj['Name']) # prints Brent
```

Il seguente esempio di codice crea un oggetto Ion da un Python`dict`.

```
a_dict = { 'GovId': 'TOYENC486FH',
           'FirstName': "Brent"
         }
ion_obj = simpleion.loads(simpleion.dumps(a_dict))

print(ion_obj['GovId']) # prints TOYENC486FH
print(ion_obj['FirstName']) # prints Brent
```

### Ottenere un dump binario Ion


```
# ion_obj is an Ion struct
print(simpleion.dumps(ion_obj)) # b'\xe0\x01\x00\xea\xee\x97\x81\x83\xde\x93\x87\xbe\x90\x85GovId\x89FirstName\xde\x94\x8a\x8bTOYENC486FH\x8b\x85Brent'
```

### Ottenere un dump di testo Ion


```
# ion_obj is an Ion struct
print(simpleion.dumps(ion_obj, binary=False)) # prints $ion_1_0 {GovId:'TOYENC486FH',FirstName:"Brent"}
```

Per ulteriori informazioni sull'utilizzo di Ion, consulta la [documentazione di Amazon Ion](http://amzn.github.io/ion-docs/) su GitHub. Per altri esempi di codice sull'utilizzo di Ion in QLDB, vedere. [Utilizzo dei tipi di dati Amazon Ion in Amazon QLDB](driver-working-with-ion.md)

# Comprendere la gestione delle sessioni con il driver in Amazon QLDB
Gestione delle sessioni con il driver

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Se hai esperienza nell'uso di un sistema di gestione di database relazionali (RDBMS), potresti avere familiarità con le connessioni simultanee. QLDB non ha lo stesso concetto di connessione RDBMS tradizionale perché le transazioni vengono eseguite con messaggi di richiesta e risposta HTTP.

*In QLDB, il concetto analogo è una sessione attiva.* Una sessione è concettualmente simile a un accesso utente: gestisce le informazioni sulle richieste di transazioni di dati a un registro. Una sessione attiva è una sessione che esegue attivamente una transazione. Può anche trattarsi di una sessione che ha recentemente terminato una transazione in cui il servizio prevede che ne avvierà immediatamente un'altra. QLDB supporta una transazione in esecuzione attiva per sessione.

Il limite di sessioni attive simultanee per registro è definito in. [Quote e limiti in Amazon QLDB](limits.md#limits.fixed) Una volta raggiunto questo limite, qualsiasi sessione che tenta di avviare una transazione genererà un errore ()`LimitExceededException`.

Per le best practice per configurare un pool di sessioni nella tua applicazione utilizzando il driver QLDB, consulta i consigli sui driver [Configurazione dell'oggetto QldbDriver](driver.best-practices.md#driver.best-practices.configuring) *Amazon* QLDB.

**Contents**
+ [

## Ciclo di vita della sessione
](#driver-session-mgmt.lifecycle)
+ [

## Scadenza della sessione
](#driver-session-mgmt.expiration)
+ [

## Gestione delle sessioni nel driver QLDB
](#driver-session-mgmt.handling)
  + [

### Panoramica del pooling delle sessioni
](#driver-session-mgmt.pooling-overview)
  + [

### Raggruppamento delle sessioni e logica delle transazioni
](#driver-session-mgmt.pooling-logic)
  + [

### Restituzione delle sessioni nel pool
](#driver-session-mgmt.pooling-return)

## Ciclo di vita della sessione


La seguente sequenza di operazioni [QLDB Session](https://docs.aws.amazon.com/qldb/latest/developerguide/API_Types_Amazon_QLDB_Session.html) API rappresenta il ciclo di vita tipico di una sessione QLDB:

1. `StartSession`

1. `StartTransaction`

1. `ExecuteStatement`

1. `CommitTransaction`

1. Ripetere i passaggi da 2 a 4 per avviare più transazioni (una transazione alla volta).

1. `EndSession`

## Scadenza della sessione


QLDB scade e scarta una sessione dopo una durata totale di **13-17 minuti, indipendentemente dal fatto che stia** eseguendo attivamente una transazione. Le sessioni possono andare perse o compromesse per una serie di motivi, come guasti hardware, errori di rete o riavvii delle applicazioni. QLDB impone una durata massima alle sessioni per garantire che l'applicazione client sia resiliente agli errori della sessione.

## Gestione delle sessioni nel driver QLDB


Sebbene sia possibile utilizzare un AWS SDK per interagire direttamente con l'API di sessione *QLDB*, ciò aggiunge complessità e richiede il calcolo di un commit digest. QLDB utilizza questo commit digest per garantire l'integrità delle transazioni. Invece di interagire direttamente con questa API, consigliamo di utilizzare il driver QLDB.

Il driver fornisce un livello di astrazione di alto livello sopra l'API dei dati transazionali. Semplifica il processo di esecuzione delle istruzioni [PartiQL](ql-reference.md) sui dati di registro gestendo [SendCommand](https://docs.aws.amazon.com/qldb/latest/developerguide/API_QLDB-Session_SendCommand.html)le chiamate API. Queste chiamate API richiedono diversi parametri che il driver gestisce automaticamente, tra cui la gestione delle sessioni, delle transazioni e la politica di riprova in caso di errori.

**Topics**
+ [

### Panoramica del pooling delle sessioni
](#driver-session-mgmt.pooling-overview)
+ [

### Raggruppamento delle sessioni e logica delle transazioni
](#driver-session-mgmt.pooling-logic)
+ [

### Restituzione delle sessioni nel pool
](#driver-session-mgmt.pooling-return)

### Panoramica del pooling delle sessioni


Nelle versioni precedenti del driver QLDB (ad esempio[, Java v1.1.0),](https://github.com/awslabs/amazon-qldb-driver-java/releases/tag/v1.1.0) fornivamo due implementazioni dell'oggetto driver: una standard, non condivisa e una. `QldbDriver` `PooledQldbDriver` Come suggerisce il nome, `PooledQldbDriver` gestisce un pool di sessioni che vengono riutilizzate tra le transazioni.

In base al feedback degli utenti, gli sviluppatori preferiscono utilizzare la funzionalità di pooling e i relativi vantaggi come impostazione predefinita, anziché utilizzare il driver standard. Pertanto, abbiamo rimosso `PooledQldbDriver` e spostato la funzionalità di pool di sessioni in. `QldbDriver` Questa modifica è inclusa nell'ultima versione di ogni driver (ad esempio, [Java](https://github.com/awslabs/amazon-qldb-driver-java/releases/tag/v2.0.0) v2.0.0).

Il driver fornisce tre livelli di astrazioni:
+ **Driver** (implementazione di driver in pool): l'astrazione di primo livello. Il driver mantiene e gestisce un pool di sessioni. Quando si chiede al driver di eseguire una transazione, il driver sceglie una sessione dal pool e la utilizza per eseguire la transazione. Se la transazione fallisce a causa di un errore di sessione (`InvalidSessionException`), il driver utilizza un'altra sessione per ritentare la transazione. In sostanza, il driver offre un'esperienza di sessione completamente gestita.
+ **Sessione**: un livello inferiore all'astrazione del driver. Una sessione è contenuta in un pool e il driver gestisce il ciclo di vita della sessione. Se una transazione fallisce, il driver la riprova fino a un numero specificato di tentativi utilizzando la stessa sessione. Ma se la sessione rileva un errore (`InvalidSessionException`), QLDB lo scarta internamente. Il driver è quindi responsabile di ottenere un'altra sessione dal pool per riprovare la transazione.
+ **Transazione**: il livello di astrazione più basso. Una transazione è contenuta in una sessione e la sessione gestisce il ciclo di vita della transazione. La sessione è responsabile di ritentare la transazione in caso di errore. La sessione assicura inoltre che non venga divulgata una transazione aperta che non è stata confermata o annullata.

Nella versione più recente di ogni driver, è possibile eseguire operazioni solo a livello di astrazione del driver. Non hai il controllo diretto sulle singole sessioni e transazioni (ovvero, non ci sono operazioni API per avviare manualmente una nuova sessione o transazione).

### Raggruppamento delle sessioni e logica delle transazioni


L'ultima versione di ogni driver non fornisce più un'implementazione non condivisa dell'oggetto driver. Per impostazione predefinita, l'`QldbDriver`oggetto gestisce il pool di sessioni. Quando si effettua una chiamata per eseguire una transazione, il driver esegue le seguenti operazioni:

1. Il driver verifica se ha raggiunto il limite del pool di sessioni. In tal caso, l'autista genera immediatamente un'eccezione. `NoSessionAvailable` In caso contrario, si procede al passaggio successivo.

1. L'autista verifica se nel pool è disponibile una sessione.
   + Se una sessione è disponibile nel pool, il driver la utilizza per eseguire una transazione.
   + Se nessuna sessione è disponibile nel pool, il driver crea una nuova sessione e la utilizza per eseguire una transazione.

1. Dopo che il driver ottiene una sessione nel passaggio 2, richiama l'`execute`operazione sull'istanza della sessione.

1. Durante il `execute` funzionamento della sessione, il driver tenta di avviare una transazione chiamando`startTransaction`.
   + Se la sessione non è valida, la `startTransaction` chiamata ha esito negativo e il driver torna al passaggio 1.
   + Se la `startTransaction` chiamata ha esito positivo, l'autista passa alla fase successiva.

1. Il driver esegue l'espressione lambda. Questa espressione lambda può contenere una o più chiamate per eseguire istruzioni PartiQL. Al termine dell'esecuzione dell'espressione lambda senza errori, il driver procede al commit della transazione.

1. Il commit della transazione può avere uno dei due risultati seguenti:
   + Il commit ha esito positivo e il driver restituisce il controllo al codice dell'applicazione.
   + Il commit ha esito negativo a causa di un conflitto OCC (Optimistic Concurrency Control). In questo caso, il driver riprova i passaggi da 4 a 6 utilizzando la stessa sessione. Il numero massimo di tentativi di nuovo tentativo è configurabile nel codice dell'applicazione. Il limite predefinito è. `4`

**Nota**  
Se `InvalidSessionException` viene generato un messaggio durante i passaggi da 4 a 6, il driver contrassegna la sessione come chiusa e torna al passaggio 1 per ripetere la transazione.  
Se viene generata un'altra eccezione durante i passaggi 4—6, il driver verifica se l'eccezione può essere ritentata. In tal caso, ritenta la transazione fino al numero specificato di tentativi. In caso contrario, propaga (si gonfia e genera) l'eccezione al codice dell'applicazione.

### Restituzione delle sessioni nel pool


Se `InvalidSessionException` si verifica un problema in qualsiasi momento nel corso di una transazione attiva, il driver non restituisce la sessione al pool. QLDB invece scarta la sessione e il driver ottiene un'altra sessione dal pool. In tutti gli altri casi, il driver restituisce la sessione al pool.

# Consigli sui driver Amazon QLDB
Consigli per i conducenti

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questa sezione descrive le best practice per configurare e utilizzare il driver Amazon QLDB per qualsiasi lingua supportata. Gli esempi di codice forniti sono specifici per Java.

Questi consigli si applicano alla maggior parte dei casi d'uso comuni, ma un'unica soluzione non è adatta a tutti. Utilizza i seguenti consigli come ritieni opportuno per la tua applicazione.

**Topics**
+ [

## Configurazione dell'oggetto QldbDriver
](#driver.best-practices.configuring)
+ [

## Riprovare con le eccezioni
](#driver.best-practices.retrying)
+ [

## Ottimizzazione delle prestazioni
](#driver.best-practices.performance)
+ [

## Esecuzione di più rendiconti per transazione
](#driver.best-practices.multiple-statements)

## Configurazione dell'oggetto QldbDriver


L'`QldbDriver`oggetto gestisce le connessioni al registro gestendo un pool di *sessioni* che vengono riutilizzate tra le transazioni. Una [sessione](driver-session-management.md) rappresenta una singola connessione al registro. QLDB supporta una transazione in esecuzione attiva per sessione.

**Importante**  
Per le versioni precedenti dei driver, la funzionalità di pool delle sessioni è ancora presente nell'`PooledQldbDriver`oggetto anziché. `QldbDriver` Se utilizzi una delle seguenti versioni, sostituisci le eventuali menzioni di `QldbDriver` con `PooledQldbDriver` per il resto di questo argomento.  


****  

| Driver | Versione | 
| --- | --- | 
| Java | 1.1.0o precedente | 
| .NET | 0.1.0-beta | 
| Node.js | 1.0.0-rc.1o prima | 
| Python | 2.0.2o prima | 
L'`PooledQldbDriver`oggetto è obsoleto nell'ultima versione dei driver. Si consiglia di eseguire l'aggiornamento alla versione più recente e di convertire tutte le istanze di to. `PooledQldbDriver` `QldbDriver`

**Configura QldbDriver come oggetto globale**

Per ottimizzare l'uso di driver e sessioni, assicuratevi che nell'istanza dell'applicazione esista solo un'istanza globale del driver. [Ad esempio, in Java, potete utilizzare framework di *iniezione delle dipendenze* come [Spring](https://spring.io/), [Google Guice](https://github.com/google/guice) o Dagger.](https://dagger.dev/) Il seguente esempio di codice mostra come configurare come singleton. `QldbDriver`

```
@Singleton
public QldbDriver qldbDriver (AWSCredentialsProvider credentialsProvider,
                                    @Named(LEDGER_NAME_CONFIG_PARAM) String ledgerName) {
    QldbSessionClientBuilder builder = QldbSessionClient.builder();
    if (null != credentialsProvider) {
        builder.credentialsProvider(credentialsProvider);
    }
    return QldbDriver.builder()
            .ledger(ledgerName)
            .transactionRetryPolicy(RetryPolicy
                .builder()
                .maxRetries(3)
                .build())
            .sessionClientBuilder(builder)
            .build();
}
```

**Configura i tentativi di riprova**

Il driver ritenta automaticamente le transazioni quando si verificano eccezioni transitorie comuni (come o). `SocketTimeoutException` `NoHttpResponseException` Per impostare il numero massimo di tentativi, è possibile utilizzare il `maxRetries` parametro dell'oggetto di `transactionRetryPolicy` configurazione durante la creazione di un'istanza di. `QldbDriver` (Per le versioni precedenti dei driver elencate nella sezione precedente, utilizzate il `retryLimit` parametro di`PooledQldbDriver`.)

Il valore predefinito di `maxRetries` è `4`.

Errori sul lato client, ad esempio l'`InvalidParameterException`impossibilità di riprovare. Quando si verificano, la transazione viene interrotta, la sessione viene restituita al pool e l'eccezione viene generata al client del driver.

**Configura il numero massimo di sessioni e transazioni simultanee**

Il numero massimo di sessioni contabili utilizzate da un'istanza di `QldbDriver` per eseguire transazioni è definito dal relativo `maxConcurrentTransactions` parametro. (Per le versioni precedenti dei driver elencate nella sezione precedente, questo valore è definito dal `poolLimit` parametro di`PooledQldbDriver`.)

Questo limite deve essere maggiore di zero e minore o uguale al numero massimo di connessioni HTTP aperte consentite dal client di sessione, come definito dallo specifico AWS SDK. Ad esempio, in Java, il numero massimo di connessioni è impostato nell'[ClientConfiguration](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/ClientConfiguration.html#getMaxConnections--)oggetto.

Il valore predefinito di `maxConcurrentTransactions` è l'impostazione di connessione massima del tuo AWS SDK.

Quando lo configuri `QldbDriver` nella tua applicazione, prendi in considerazione le seguenti considerazioni sulla scalabilità:
+ Il pool deve avere sempre almeno lo stesso numero di sessioni quante sono le transazioni in esecuzione simultanea che si prevede di effettuare.
+ In un modello multithread in cui un thread supervisore delega ai thread di lavoro, il driver dovrebbe avere un numero di sessioni almeno pari al numero di thread di lavoro. Altrimenti, al picco di carico, i thread aspetteranno in fila una sessione disponibile.
+ Il limite di servizio delle sessioni attive simultanee per registro è definito in. [Quote e limiti in Amazon QLDB](limits.md#limits.fixed) Assicurati di non configurare più di questo limite di sessioni simultanee da utilizzare per un singolo registro tra tutti i client.

## Riprovare con le eccezioni


Quando riprovi a risolvere le eccezioni che si verificano in QLDB, prendi in considerazione i seguenti consigli.

**Riprovare OccConflictException**

Le eccezioni ai conflitti di *Optimistic Concurrency Control* (OCC) si verificano quando i dati a cui accede la transazione sono cambiati dall'inizio della transazione. QLDB genera questa eccezione durante il tentativo di confermare la transazione. Il driver ritenta la transazione fino a un numero massimo di volte pari a quello configurato. `maxRetries`

Per ulteriori informazioni su OCC e sulle migliori pratiche per l'utilizzo degli indici per limitare i conflitti OCC, vedere. [Modello di concorrenza Amazon QLDB](concurrency.md)

**Riprovare con altre eccezioni diverse da QldbDriver**

Per riprovare una transazione all'esterno del driver quando durante l'esecuzione vengono generate eccezioni personalizzate e definite dall'applicazione, è necessario eseguire il wrapping della transazione. Ad esempio, in Java, il codice seguente mostra come utilizzare la libreria [Reslience4J](https://resilience4j.readme.io/) per ritentare una transazione in QLDB.

```
private final RetryConfig retryConfig = RetryConfig.custom()
        .maxAttempts(MAX_RETRIES)
        .intervalFunction(IntervalFunction.ofExponentialRandomBackoff())
        // Retry this exception
        .retryExceptions(InvalidSessionException.class, MyRetryableException.class)
        // But fail for any other type of exception extended from RuntimeException
        .ignoreExceptions(RuntimeException.class)
        .build();

// Method callable by a client
public void myTransactionWithRetries(Params params) {
    Retry retry = Retry.of("registerDriver", retryConfig);

    Function<Params, Void> transactionFunction = Retry.decorateFunction(
            retry,
            parameters ->  transactionNoReturn(params));
    transactionFunction.apply(params);
}

private Void transactionNoReturn(Params params) {
    try (driver.execute(txn -> {
            // Transaction code
        });
    }
    return null;
}
```

**Nota**  
Ritentare una transazione al di fuori del driver QLDB ha un effetto moltiplicatore. Ad esempio, se `QldbDriver` è configurato per riprovare tre volte e anche la logica di riprova personalizzata riprova tre volte, la stessa transazione può essere ritentata fino a nove volte.

**Rendere le transazioni idempotenti**

È consigliabile 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.

Per ulteriori informazioni, consulta [Modello di concorrenza Amazon QLDB](concurrency.md#concurrency.idempotent).

## Ottimizzazione delle prestazioni


Per ottimizzare le prestazioni quando esegui transazioni utilizzando il driver, prendi le seguenti considerazioni:
+ L'`execute`operazione effettua sempre un minimo di tre chiamate `SendCommand` API a QLDB, inclusi i seguenti comandi:

  1. `StartTransaction`

  1. `ExecuteStatement`

     Questo comando viene richiamato per ogni istruzione PartiQL eseguita nel `execute` blocco.

  1. `CommitTransaction`

  Considerate il numero totale di chiamate API effettuate quando calcolate il carico di lavoro complessivo dell'applicazione.
+ In generale, consigliamo di iniziare con uno scrittore a thread singolo e di ottimizzare le transazioni raggruppando più istruzioni all'interno di una singola transazione. Massimizza le quote relative alle dimensioni delle transazioni, alle dimensioni dei documenti e al numero di documenti per transazione, come definito in. [Quote e limiti in Amazon QLDB](limits.md#limits.fixed)
+ Se il batching non è sufficiente per carichi di transazioni di grandi dimensioni, puoi provare il multithreading aggiungendo altri writer. Tuttavia, è necessario considerare attentamente i requisiti dell'applicazione per il sequenziamento di documenti e transazioni e la complessità aggiuntiva che ciò comporta.

## Esecuzione di più rendiconti per transazione


Come descritto nella [sezione precedente](#driver.best-practices.performance), è possibile eseguire più istruzioni per transazione per ottimizzare le prestazioni dell'applicazione. Nel seguente esempio di codice, si esegue una query su una tabella e quindi si aggiorna un documento in quella tabella all'interno di una transazione. A tale scopo, è necessario passare un'espressione lambda all'`execute`operazione.

------
#### [ Java ]

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

------
#### [ .NET ]

```
// 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 async Task<bool> InsureVehicle(IAsyncQldbDriver driver, string vin)
{
    ValueFactory valueFactory = new ValueFactory();
    IIonValue ionVin = valueFactory.NewString(vin);

    return await driver.Execute(async txn =>
    {
        // Check if the vehicle is insured.
        Amazon.QLDB.Driver.IAsyncResult result = await txn.Execute(
            "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", ionVin);

        if (await result.CountAsync() > 0)
        {
            // If the vehicle is not insured, insure it.
            await txn.Execute(
                "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", ionVin);
            return true;
        }
        return false;
    });
}
```

------
#### [ Go ]

```
// 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.
func InsureCar(driver *qldbdriver.QLDBDriver, vin string) (bool, error) {
    insured, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {

        result, err := txn.Execute(
            "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)
        if err != nil {
            return false, err
        }

        hasNext := result.Next(txn)
        if !hasNext && result.Err() != nil {
            return false, result.Err()
        }

        if hasNext {
            _, err = txn.Execute(
                "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin)
            if err != nil {
                return false, err
            }
            return true, nil
        }
        return false, nil
    })
    if err != nil {
        panic(err)
    }

    return insured.(bool), err
}
```

------
#### [ Node.js ]

```
// 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.
async function insureCar(driver: QldbDriver, vin: string): Promise<boolean> {

    return await driver.executeLambda(async (txn: TransactionExecutor) => {
        const results: dom.Value[] = (await txn.execute(
            "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)).getResultList();

        if (results.length > 0) {
            await txn.execute(
                "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin);
            return true;
        }
        return false;
    });
};
```

------
#### [ Python ]

```
# 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.

def do_insure_car(transaction_executor, vin):
    cursor = transaction_executor.execute_statement(
        "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)
    first_record = next(cursor, None)
    if first_record:
        transaction_executor.execute_statement(
            "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin)
        return True
    else:
        return False

def insure_car(qldb_driver, vin_to_insure):
    return qldb_driver.execute_lambda(
        lambda executor: do_insure_car(executor, vin_to_insure))
```

------

L'`execute`operazione del driver avvia implicitamente una sessione e una transazione in quella sessione. Ogni istruzione eseguita nell'espressione lambda è inclusa nella transazione. Dopo l'esecuzione di tutte le istruzioni, il driver esegue automaticamente la transazione. Se un'istruzione fallisce dopo l'esaurimento del limite di tentativi automatici, la transazione viene interrotta.

**Propaga le eccezioni in una transazione**

Quando si eseguono più rendiconti per transazione, in genere non consigliamo di catturare e ingoiare eccezioni all'interno della transazione.

Ad esempio, in Java, il seguente programma rileva qualsiasi istanza di`RuntimeException`, registra l'errore e continua. Questo esempio di codice è considerato una pratica scorretta perché la transazione ha esito positivo anche quando l'`UPDATE`istruzione ha esito negativo. Pertanto, il client potrebbe presumere che l'aggiornamento abbia avuto esito positivo quando non lo è stato.

**avvertimento**  
Non utilizzare questo esempio di codice. Viene fornito per mostrare un esempio anti-pattern considerato una cattiva pratica.

```
// DO NOT USE this code example because it is considered bad practice
public static void main(final String... args) {
    ConnectToLedger.getDriver().execute(txn -> {
        final Result selectTableResult = txn.execute("SELECT * FROM Vehicle WHERE VIN ='123456789'");
        // Catching an error inside the transaction is an anti-pattern because the operation might
        // not succeed.
        // In this example, the transaction succeeds even when the update statement fails.
        // So, the client might assume that the update succeeded when it didn't.
        try {
            processResults(selectTableResult);
            String model = // some code that extracts the model
            final Result updateResult = txn.execute("UPDATE Vehicle SET model = ? WHERE VIN = '123456789'",
                    Constants.MAPPER.writeValueAsIonValue(model));
        } catch (RuntimeException e) {
            log.error("Exception when updating the Vehicle table {}", e.getMessage());
        }
    });
    log.info("Vehicle table updated successfully.");
}
```

Propaga invece (ribolle up) l'eccezione. Se una parte della transazione fallisce, lascia che l'`execute`operazione interrompa la transazione in modo che il client possa gestire l'eccezione di conseguenza.

# Comprendere la politica dei nuovi tentativi con il driver in Amazon QLDB
Politica sui nuovi tentativi del conducente

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Il driver Amazon QLDB utilizza una politica di nuovi tentativi per gestire le eccezioni transitorie ritentando in modo trasparente una transazione non riuscita. Queste eccezioni, come e, in genere si correggono automaticamente dopo un certo `CapacityExceededException` periodo di `RateExceededException` tempo. Se la transazione fallita con l'eccezione viene ritentata dopo un periodo di tempo adeguato, è probabile che abbia successo. Questo aiuta a migliorare la stabilità dell'applicazione che utilizza QLDB.

**Topics**
+ [

## Tipi di errori riutilizzabili
](#driver-retry-policy.retryable-errors)
+ [

## Criteri di riprova predefiniti
](#driver-retry-policy.default)

## Tipi di errori riutilizzabili


Il driver ritenta automaticamente una transazione se e solo se si verifica una delle seguenti eccezioni durante un'operazione all'interno di tale transazione:
+ [CapacityExceededException](driver-errors.md)— Restituito quando la richiesta supera la capacità di elaborazione del registro.
+ [InvalidSessionException](driver-errors.md)— Restituito quando una sessione non è più valida o se la sessione non esiste.
+ [LimitExceededException](driver-errors.md)— Restituito se viene superato un limite di risorse, ad esempio il numero di sessioni attive.
+ [OccConflictException](concurrency.md)— Restituito quando una transazione non può essere scritta sul journal a causa di un errore nella fase di verifica dell'*Optimistic Concurrency Control* (OCC).
+ [RateExceededException](driver-errors.md)— Restituito quando la frequenza delle richieste supera il throughput consentito.

## Criteri di riprova predefiniti


La politica sui nuovi tentativi consiste in una condizione di nuovo tentativo e in una strategia di backoff. La condizione di nuovo tentativo definisce quando una transazione deve essere ritentata, mentre la strategia di backoff definisce il tempo di attesa prima di riprovare la transazione.

Quando si crea un'istanza del driver, la politica di riprova predefinita prevede di riprovare fino a quattro volte e di utilizzare una strategia di backoff esponenziale. La strategia di backoff esponenziale utilizza un ritardo minimo di 10 millisecondi e un ritardo massimo di 5000 millisecondi, a parità di jitter. Se la transazione non può essere completata correttamente nell'ambito della politica relativa ai nuovi tentativi, ti consigliamo di provare la transazione in un altro momento.

Il concetto di backoff esponenziale consiste nell'utilizzare tempi di attesa progressivamente più lunghi tra un tentativo e l'altro per risposte di errore consecutive. [Per ulteriori informazioni, consulta il post di AWS blog Exponential Backoff and Jitter.](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)

# Errori comuni del driver Amazon QLDB
Errori comuni

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

[Questa sezione descrive gli errori di runtime che possono essere generati dal driver Amazon QLDB durante l'interazione con l'API di sessione QLDB.](https://docs.aws.amazon.com/qldb/latest/developerguide/API_Types_Amazon_QLDB_Session.html)

Di seguito è riportato un elenco di eccezioni comuni restituite dal driver. Ogni eccezione include il messaggio di errore specifico, seguito da una breve descrizione e suggerimenti per possibili soluzioni.<a name="driver-errors.varlist"></a>

**CapacityExceededException**  
Messaggio: capacità superata  
Amazon QLDB ha rifiutato la richiesta perché superava la capacità di elaborazione del registro. QLDB impone un limite di scalabilità interno per registro per mantenere l'integrità e le prestazioni del servizio. Questo limite varia a seconda delle dimensioni del carico di lavoro di ogni singola richiesta. Ad esempio, una richiesta può comportare un carico di lavoro maggiore se esegue transazioni di dati inefficienti, ad esempio scansioni di tabelle risultanti da una query qualificata non indicizzata.  
Ti consigliamo di attendere prima di riprovare la richiesta. Se la tua applicazione riscontra costantemente questa eccezione, ottimizza i rendiconti e riduci la frequenza e il volume delle richieste inviate al registro. Esempi di ottimizzazione dei rendiconti includono l'esecuzione di un minor numero di istruzioni per transazione e l'ottimizzazione degli indici delle tabelle. Per informazioni su come ottimizzare le istruzioni ed evitare le scansioni delle tabelle, consulta. [Ottimizzazione delle prestazioni delle query](working.optimize.md)  
Si consiglia inoltre di utilizzare la versione più recente del driver QLDB. Il driver ha una politica di riprova predefinita che utilizza [Exponential Backoff e Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) per riprovare automaticamente eccezioni come questa. Il concetto di backoff esponenziale consiste nell'utilizzare tempi di attesa progressivamente più lunghi tra un tentativo e l'altro per risposte di errore consecutive.

**InvalidSessionException**  
Messaggio: La transazione è scaduta *transactionId*  
Una transazione ha superato la sua durata massima. Una transazione può durare fino a 30 secondi prima di essere confermata. Dopo questo limite di timeout, qualsiasi lavoro svolto sulla transazione viene rifiutato e QLDB scarta la sessione. Questo limite protegge il cliente dalla perdita di sessioni avviando le transazioni e non eseguendole o annullandole.  
Se questa è un'eccezione comune nella tua applicazione, è probabile che le transazioni richiedano semplicemente troppo tempo per essere eseguite. Se l'esecuzione delle transazioni richiede più di 30 secondi, ottimizza i rendiconti per velocizzarle. Esempi di ottimizzazione dei rendiconti includono l'esecuzione di un minor numero di istruzioni per transazione e l'ottimizzazione degli indici delle tabelle. Per ulteriori informazioni, consulta [Ottimizzazione delle prestazioni delle query](working.optimize.md).

**InvalidSessionException**  
Messaggio: la sessione è scaduta *sessionId*  
QLDB ha scartato la sessione perché ha superato la sua durata totale massima. QLDB elimina le sessioni dopo 13-17 minuti, indipendentemente da una transazione attiva. Le sessioni possono andare perse o compromesse per una serie di motivi, come guasti hardware, errori di rete o riavvii delle applicazioni. Pertanto, QLDB impone una durata massima delle sessioni per garantire che il software client sia resiliente agli errori delle sessioni.  
Se riscontri questa eccezione, ti consigliamo di acquisire una nuova sessione e ritentare la transazione. Si consiglia inoltre di utilizzare la versione più recente del driver QLDB, che gestisce il pool di sessioni e il relativo stato per conto dell'applicazione.

**InvalidSessionException**  
Messaggio: sessione inesistente  
Il client ha provato a effettuare transazioni con QLDB utilizzando una sessione che non esiste. Supponendo che il client stia utilizzando una sessione che esisteva in precedenza, la sessione potrebbe non esistere più a causa di uno dei seguenti motivi:  
+ Se una sessione è coinvolta in un errore interno del server (ovvero un errore con codice di risposta HTTP 500), QLDB potrebbe scegliere di ignorare completamente la sessione, anziché consentire al cliente di effettuare transazioni con una sessione di stato incerto. Quindi, qualsiasi tentativo di riprovare quella sessione fallisce con questo errore.
+ Le sessioni scadute vengono infine dimenticate da QLDB. Quindi, qualsiasi tentativo di continuare a utilizzare la sessione genera questo errore, anziché quello iniziale. `InvalidSessionException`
Se riscontri questa eccezione, ti consigliamo di acquisire una nuova sessione e riprovare la transazione. Si consiglia inoltre di utilizzare la versione più recente del driver QLDB, che gestisce il pool di sessioni e il relativo stato per conto dell'applicazione.

**RateExceededException**  
Messaggio: la velocità è stata superata  
QLDB limitava un client in base all'identità del chiamante. [QLDB applica la limitazione per regione e per account utilizzando un algoritmo di throttling del token bucket.](https://en.wikipedia.org/wiki/Token_bucket) QLDB lo fa per migliorare le prestazioni del servizio e garantire un utilizzo equo per tutti i clienti QLDB. Ad esempio, il tentativo di acquisire un gran numero di sessioni simultanee utilizzando l'`StartSessionRequest`operazione potrebbe comportare un rallentamento.  
[Per mantenere l'integrità dell'applicazione e mitigare ulteriori limitazioni, puoi riprovare questa eccezione utilizzando Exponential Backoff e Jitter.](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) Il concetto di backoff esponenziale consiste nell'utilizzare tempi di attesa progressivamente più lunghi tra un tentativo e l'altro per risposte di errore consecutive. Si consiglia di utilizzare la versione più recente del driver QLDB. Il driver ha una politica di riprova predefinita che utilizza il backoff e il jitter esponenziali per riprovare automaticamente eccezioni come questa.  
L'ultima versione del driver QLDB può essere utile anche se l'applicazione viene costantemente limitata da QLDB per le chiamate. `StartSessionRequest` Il driver gestisce un pool di sessioni che vengono riutilizzate tra le transazioni, il che può aiutare a ridurre il numero di chiamate effettuate dall'applicazione. `StartSessionRequest` [Per richiedere un aumento dei limiti di limitazione delle API, contatta il Centro.Supporto AWS](https://console.aws.amazon.com/support/home#/)

**LimitExceededException**  
Messaggio: è stato superato il limite di sessione  
Un registro ha superato la quota (nota anche come *limite*) sul numero di sessioni attive. Questa quota è definita in. [Quote e limiti in Amazon QLDB](limits.md) Il conteggio delle sessioni attive di un libro mastro alla fine è costante e i libri contabili che si avvicinano costantemente alla quota potrebbero periodicamente vedere questa eccezione.  
Per mantenere l'integrità dell'applicazione, ti consigliamo di riprovare questa eccezione. Per evitare questa eccezione, assicurati di non aver configurato più di 1.500 sessioni simultanee da utilizzare per un singolo registro su tutti i client. Ad esempio, puoi utilizzare il [maxConcurrentTransactions](https://github.com/awslabs/amazon-qldb-driver-java/blob/master/src/main/java/software/amazon/qldb/QldbDriverBuilder.java#L125)metodo del [driver Amazon QLDB per Java per](https://github.com/awslabs/amazon-qldb-driver-java/) configurare il numero massimo di sessioni disponibili in un'istanza di driver.

**QldbClientException**  
Messaggio: un risultato in streaming è valido solo quando la transazione principale è aperta  
La transazione è chiusa e non può essere utilizzata per recuperare i risultati da QLDB. Una transazione si chiude quando viene confermata o annullata.  
Questa eccezione si verifica quando il client lavora direttamente con l'`Transaction`oggetto e sta cercando di recuperare i risultati da QLDB dopo aver eseguito o annullato una transazione. Per mitigare questo problema, il cliente deve leggere i dati prima di chiudere la transazione.

# Guida introduttiva ad Amazon QLDB utilizzando un tutorial applicativo di esempio
Esempio di tutorial applicativo

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo tutorial, utilizzi il driver Amazon QLDB con AWS un SDK per creare un registro QLDB e compilarlo con dati di esempio. Il driver consente all'applicazione di interagire con QLDB utilizzando l'API dei dati transazionali. L' AWS SDK supporta l'interazione con l'API di gestione delle risorse QLDB.

Il registro di esempio creato in questo scenario è un database del Dipartimento dei veicoli a motore (DMV) che tiene traccia delle informazioni storiche complete sulle immatricolazioni dei veicoli. I seguenti argomenti spiegano come aggiungere immatricolazioni di veicoli, modificarle e visualizzare la cronologia delle modifiche a tali immatricolazioni. Questa guida mostra anche come verificare crittograficamente un documento di immatricolazione e si conclude con la pulizia delle risorse e l'eliminazione del registro di esempio.

Questo tutorial applicativo di esempio è disponibile per i seguenti linguaggi di programmazione.

**Topics**
+ [Tutorial su Java](getting-started.java.tutorial.md)
+ [Tutorial su Node.js](getting-started.nodejs.tutorial.md)
+ [Tutorial di Python](getting-started.python.tutorial.md)

# Tutorial Java per Amazon QLDB
Tutorial su Java

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questa implementazione dell'applicazione di esempio del tutorial, utilizzi il driver Amazon QLDB con AWS SDK per Java per creare un registro QLDB e compilarlo con dati di esempio.

Mentre segui questo tutorial, puoi fare riferimento all'API Reference per le operazioni [AWS SDK per Java API](https://docs.aws.amazon.com/sdk-for-java/latest/reference/) di gestione. Per le operazioni sui dati transazionali, puoi fare riferimento al QLDB Driver for Java API [Reference](https://javadoc.io/doc/software.amazon.qldb/amazon-qldb-driver-java/latest/index.html).

**Nota**  
Ove applicabile, alcuni passaggi del tutorial contengono comandi o esempi di codice diversi per ogni versione principale supportata del driver QLDB per Java.

**Topics**
+ [

# Installazione dell'applicazione di esempio Amazon QLDB Java
](sample-app.java.md)
+ [

# Fase 1: Creare un nuovo registro
](getting-started.java.step-1.md)
+ [

# Passaggio 2: verifica la connettività al registro
](getting-started.java.step-2.md)
+ [

# Passaggio 3: Creare tabelle, indici e dati di esempio
](getting-started.java.step-3.md)
+ [

# Passaggio 4: interrogare le tabelle in un libro mastro
](getting-started.java.step-4.md)
+ [

# Fase 5: Modificare i documenti in un libro mastro
](getting-started.java.step-5.md)
+ [

# Fase 6: Visualizzare la cronologia delle revisioni di un documento
](getting-started.java.step-6.md)
+ [

# Passaggio 7: Verificare un documento in un libro mastro
](getting-started.java.step-7.md)
+ [

# Fase 8: Esportazione e convalida dei dati del diario in un libro mastro
](getting-started.java.step-8.md)
+ [

# Passaggio 9 (opzionale): Pulisci le risorse
](getting-started.java.step-9.md)

# Installazione dell'applicazione di esempio Amazon QLDB Java
Installazione dell'applicazione di esempio

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questa sezione descrive come installare ed eseguire l'applicazione di esempio Amazon QLDB fornita per step-by-step il tutorial Java. Il caso d'uso di questa applicazione di esempio è un database del Dipartimento dei veicoli a motore (DMV) che tiene traccia delle informazioni storiche complete sulle immatricolazioni dei veicoli.

[L'applicazione di esempio DMV per Java è open source nel repository aws-samples/ -java. GitHub amazon-qldb-dmv-sample](https://github.com/aws-samples/amazon-qldb-dmv-sample-java)

## Prerequisiti


Prima di iniziare, assicurati di aver completato il driver QLDB per Java. [Prerequisiti](getting-started.java.md#getting-started.java.prereqs) Questo include gli output seguenti:

1. Iscriviti per. AWS

1. Crea un utente con le autorizzazioni QLDB appropriate. Per completare tutti i passaggi di questo tutorial, è necessario l'accesso amministrativo completo alla risorsa di registro tramite l'API QLDB.

1. Se utilizzi un IDE diverso da quello AWS Cloud9, installa Java e concedi l'accesso programmatico per lo sviluppo.

## Installazione


I passaggi seguenti descrivono come scaricare e configurare l'applicazione di esempio con un ambiente di sviluppo locale. In alternativa, è possibile automatizzare la configurazione dell'applicazione di esempio utilizzandola AWS Cloud9 come IDE e un CloudFormation modello per fornire le risorse di sviluppo.

### Ambiente di sviluppo locale


Queste istruzioni descrivono come scaricare e installare l'applicazione di esempio Java QLDB utilizzando risorse e ambiente di sviluppo propri.

**Per scaricare ed eseguire l'applicazione di esempio**

1. Immettete il seguente comando da cui clonare l'applicazione di GitHub esempio.

------
#### [ 2.x ]

   ```
   git clone https://github.com/aws-samples/amazon-qldb-dmv-sample-java.git
   ```

------
#### [ 1.x ]

   ```
   git clone -b v1.2.0 https://github.com/aws-samples/amazon-qldb-dmv-sample-java.git
   ```

------

   Questo pacchetto include la configurazione Gradle e il codice completo di. [Tutorial su Java](getting-started.java.tutorial.md)

1. Carica ed esegui l'applicazione fornita.
   + Se stai usando Eclipse:

     1. Avvia Eclipse e, nel menu **Eclipse**, scegli **File**, **Importa**, quindi Progetto **Gradle esistente**.

     1. Nella directory principale del progetto, sfoglia e seleziona la directory dell'applicazione che contiene il file. `build.gradle` Quindi, scegliete **Fine** per utilizzare le impostazioni Gradle predefinite per l'importazione. 

     1. Puoi provare a eseguire il `ListLedgers` programma come esempio. Apri il menu contestuale (fai clic con il pulsante destro del mouse) per il `ListLedgers.java` file e scegli **Esegui come applicazione Java**.
   + Se utilizzi IntelliJ:

     1. **Avviare IntelliJ e nel menu **IntelliJ** scegliere **File**, quindi Apri.**

     1. Nella directory principale del progetto, sfogliate e selezionate la directory dell'applicazione che contiene il `build.gradle` file. Quindi, scegliete **OK**. Mantenete le impostazioni predefinite e scegliete nuovamente **OK**. 

     1. Puoi provare a eseguire il `ListLedgers` programma come esempio. Apri il menu contestuale (fai clic con il pulsante destro del mouse) per il `ListLedgers.java` file e scegli **Esegui 'ListLedgers'**.

1. Procedi con [Fase 1: Creare un nuovo registro](getting-started.java.step-1.md) l'avvio del tutorial e la creazione di un libro mastro.

### AWS Cloud9


Queste istruzioni descrivono come automatizzare la configurazione dell'applicazione di esempio per la registrazione di veicoli Amazon QLDB per Java, [AWS Cloud9](https://aws.amazon.com/cloud9)utilizzandola come IDE. In questa guida, utilizzi un [CloudFormation](https://aws.amazon.com/cloudformation)modello per fornire le tue risorse di sviluppo.

Per ulteriori informazioni in merito AWS Cloud9, consulta la [Guida AWS Cloud9 per l'utente](https://docs.aws.amazon.com/cloud9/latest/user-guide/). Per ulteriori informazioni su CloudFormation, consulta la [Guida per l'utente di AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/).

**Topics**
+ [

#### Parte 1: Fornisci le tue risorse
](#sample-app.java.cfn-ac9.step-1)
+ [

#### Parte 2: configura il tuo IDE
](#sample-app.java.cfn-ac9.step-2)
+ [

#### Parte 3: Esecuzione dell'applicazione di esempio QLDB DMV
](#sample-app.java.cfn-ac9.step-3)

#### Parte 1: Fornisci le tue risorse


In questo primo passaggio, devi CloudFormation fornire le risorse necessarie per configurare il tuo ambiente di sviluppo con l'applicazione di esempio Amazon QLDB.

**Per aprire la CloudFormation console e caricare il modello di applicazione di esempio QLDB**

1. [Accedi Console di gestione AWS e apri la CloudFormation console all'indirizzo https://console.aws.amazon.com /cloudformation.](https://console.aws.amazon.com/cloudformation/)

   Passa a una regione che supporta QLDB. Per un elenco completo, consulta gli [endpoint e le quote di Amazon QLDB](https://docs.aws.amazon.com/general/latest/gr/qldb.html) nel. *Riferimenti generali di AWS* La seguente schermata Console di gestione AWS mostra gli Stati Uniti orientali (Virginia settentrionale) come selezionati. Regione AWS  
![\[Console di gestione AWS mostrando Stati Uniti orientali (Virginia settentrionale) come selezionato. Regione AWS\]](http://docs.aws.amazon.com/it_it/qldb/latest/developerguide/images/cfn-ac9/aws-region-us-east-1.png)

1. Sulla CloudFormation console, scegli **Crea stack**, quindi scegli **Con nuove risorse (standard)**.

1. Nella pagina **Crea stack**, sotto **Specificare modello**, scegli l'URL di **Amazon S3**.

1. **Inserisci il seguente URL e scegli Avanti.**

   ```
   https://amazon-qldb-assets.s3.amazonaws.com/templates/QLDB-DMV-SampleApp.yml
   ```

1. Immettete un **nome per lo stack** (ad esempio**qldb-sample-app**) e scegliete **Avanti**.

1. Puoi aggiungere qualsiasi tag appropriato e mantenere le opzioni predefinite. Quindi scegli **Successivo**.

1. Controlla le impostazioni dello stack e scegli **Crea pila**. Il CloudFormation completamento dello script potrebbe richiedere alcuni minuti.

   Questo script effettua il provisioning del tuo AWS Cloud9 ambiente con un'istanza Amazon Elastic Compute Cloud (Amazon EC2) associata che usi per eseguire l'applicazione di esempio QLDB in questo tutorial. Inoltre, clona il repository [amazon-qldb-dmv-sampleaws-samples/](https://github.com/aws-samples/amazon-qldb-dmv-sample-java/) -java dal tuo ambiente di sviluppo. GitHub AWS Cloud9 

#### Parte 2: configura il tuo IDE


In questo passaggio, finisci la configurazione del tuo ambiente di sviluppo cloud. Scarichi ed esegui uno script di shell fornito per configurare l' AWS Cloud9 IDE con le dipendenze dell'applicazione di esempio.

**Per configurare il tuo ambiente AWS Cloud9**

1. Apri la AWS Cloud9 console all'indirizzo [https://console.aws.amazon.com/cloud9/](https://console.aws.amazon.com/cloud9/).

1. **In **I tuoi ambienti**, individua la scheda per l'ambiente denominato **QLDB DMV Sample** Application e scegli Open IDE.** Il caricamento dell'ambiente potrebbe richiedere un minuto all'avvio dell'istanza sottostante. EC2 

   L' AWS Cloud9 ambiente è preconfigurato con le dipendenze di sistema necessarie per eseguire il tutorial. Nel riquadro di navigazione **Ambiente** della console, verifica che sia presente una cartella denominata. `QLDB DMV Sample Application` La seguente schermata della AWS Cloud9 console mostra il riquadro della cartella dell'ambiente QLDB DMV Sample Application.  
![\[AWS Cloud9 console che mostra il riquadro della cartella dell'ambiente QLDB DMV Sample Application.\]](http://docs.aws.amazon.com/it_it/qldb/latest/developerguide/images/cfn-ac9/cloud9-folders.png)

   Se non vedi un pannello di navigazione, attiva la scheda **Ambiente** sul lato sinistro della console. Se non vedi alcuna cartella nel riquadro, abilita **Show Environment Root** usando l'icona delle impostazioni ()![\[Settings icon\]](http://docs.aws.amazon.com/it_it/qldb/latest/developerguide/images/settings.png).

1. Nel riquadro inferiore della console, dovresti vedere una finestra di `bash` terminale aperta. Se non la vedi, scegli **Nuovo terminale** dal menu **Finestra** nella parte superiore della console.

1. Successivamente, scarica ed esegui uno script di installazione per installare OpenJDK 8 e, se applicabile, controlla il ramo appropriato dal repository Git. Nel AWS Cloud9 terminale che hai creato nel passaggio precedente, esegui i due comandi seguenti nell'ordine:

------
#### [ 2.x ]

   ```
   aws s3 cp s3://amazon-qldb-assets/setup-scripts/dmv-setup-v2.sh .
   ```

   ```
   sh dmv-setup-v2.sh
   ```

------
#### [ 1.x ]

   ```
   aws s3 cp s3://amazon-qldb-assets/setup-scripts/dmv-setup.sh .
   ```

   ```
   sh dmv-setup.sh
   ```

------

   Al termine, dovresti vedere il seguente messaggio stampato nel terminale:

   ```
   ** DMV Sample App setup completed , enjoy!! **
   ```

1. Prenditi un momento per sfogliare il codice dell'applicazione di esempio AWS Cloud9, in particolare nel seguente percorso di directory:`src/main/java/software/amazon/qldb/tutorial`.

#### Parte 3: Esecuzione dell'applicazione di esempio QLDB DMV


In questo passaggio, imparerai come eseguire le attività dell'applicazione di esempio Amazon QLDB DMV utilizzando. AWS Cloud9 Per eseguire il codice di esempio, torna al tuo AWS Cloud9 terminale o crea una nuova finestra di terminale come hai fatto nella *Parte 2: Configurazione* dell'IDE.

**Per eseguire l'applicazione di esempio**

1. Esegui il seguente comando nel tuo terminale per passare alla directory principale del progetto:

   ```
   cd ~/environment/amazon-qldb-dmv-sample-java
   ```

   Assicurati di eseguire gli esempi nel seguente percorso di directory.

   ```
   /home/ec2-user/environment/amazon-qldb-dmv-sample-java/
   ```

1. Il comando seguente mostra la sintassi Gradle per eseguire ogni attività.

   ```
   ./gradlew run -Dtutorial=Task
   ```

   Ad esempio, esegui il comando seguente per elencare tutti i registri nella tua regione Account AWS e nella regione corrente.

   ```
   ./gradlew run -Dtutorial=ListLedgers
   ```

1. Procedi con [Fase 1: Creare un nuovo registro](getting-started.java.step-1.md) l'avvio del tutorial e la creazione di un libro mastro.

1. (Facoltativo) Dopo aver completato il tutorial, pulisci CloudFormation le risorse se non ti servono più.

   1. Apri la CloudFormation console in [https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/) ed elimina lo stack che hai creato nella *Parte 1:* Fornisci le tue risorse.

   1. Elimina anche lo AWS Cloud9 stack creato dal modello per te. CloudFormation 

# Fase 1: Creare un nuovo registro


**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo passaggio, crei un nuovo registro Amazon QLDB denominato. `vehicle-registration`

**Per creare un nuovo libro mastro**

1. Esamina il seguente file (`Constants.java`), che contiene valori costanti utilizzati da tutti gli altri programmi di questo tutorial.

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazon.ion.IonSystem;
   import com.amazon.ion.system.IonSystemBuilder;
   import com.fasterxml.jackson.databind.SerializationFeature;
   import com.fasterxml.jackson.dataformat.ion.IonObjectMapper;
   import com.fasterxml.jackson.dataformat.ion.ionvalue.IonValueMapper;
   
   /**
    * Constant values used throughout this tutorial.
    */
   public final class Constants {
       public static final int RETRY_LIMIT = 4;
       public static final String LEDGER_NAME = "vehicle-registration";
       public static final String STREAM_NAME = "vehicle-registration-stream";
       public static final String VEHICLE_REGISTRATION_TABLE_NAME = "VehicleRegistration";
       public static final String VEHICLE_TABLE_NAME = "Vehicle";
       public static final String PERSON_TABLE_NAME = "Person";
       public static final String DRIVERS_LICENSE_TABLE_NAME = "DriversLicense";
       public static final String VIN_INDEX_NAME = "VIN";
       public static final String PERSON_GOV_ID_INDEX_NAME = "GovId";
       public static final String VEHICLE_REGISTRATION_LICENSE_PLATE_NUMBER_INDEX_NAME = "LicensePlateNumber";
       public static final String DRIVER_LICENSE_NUMBER_INDEX_NAME = "LicenseNumber";
       public static final String DRIVER_LICENSE_PERSONID_INDEX_NAME = "PersonId";
       public static final String JOURNAL_EXPORT_S3_BUCKET_NAME_PREFIX = "qldb-tutorial-journal-export";
       public static final String USER_TABLES = "information_schema.user_tables";
       public static final String LEDGER_NAME_WITH_TAGS = "tags";
       public static final IonSystem SYSTEM = IonSystemBuilder.standard().build();
       public static final IonObjectMapper MAPPER = new IonValueMapper(SYSTEM);
   
       private Constants() { }
   
       static {
           MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
       }
   }
   ```

------
#### [ 1.x ]

**Importante**  
Per il pacchetto Amazon Ion, devi utilizzare lo spazio dei nomi `com.amazon.ion` nella tua applicazione. AWS SDK per Java Dipende da un altro pacchetto Ion nello spazio dei nomi`software.amazon.ion`, ma si tratta di un pacchetto legacy non compatibile con il driver QLDB.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazon.ion.IonSystem;
   import com.amazon.ion.system.IonSystemBuilder;
   import com.fasterxml.jackson.databind.SerializationFeature;
   import com.fasterxml.jackson.dataformat.ion.IonObjectMapper;
   import com.fasterxml.jackson.dataformat.ion.ionvalue.IonValueMapper;
   
   /**
    * Constant values used throughout this tutorial.
    */
   public final class Constants {
       public static final int RETRY_LIMIT = 4;
       public static final String LEDGER_NAME = "vehicle-registration";
       public static final String STREAM_NAME = "vehicle-registration-stream";
       public static final String VEHICLE_REGISTRATION_TABLE_NAME = "VehicleRegistration";
       public static final String VEHICLE_TABLE_NAME = "Vehicle";
       public static final String PERSON_TABLE_NAME = "Person";
       public static final String DRIVERS_LICENSE_TABLE_NAME = "DriversLicense";
       public static final String VIN_INDEX_NAME = "VIN";
       public static final String PERSON_GOV_ID_INDEX_NAME = "GovId";
       public static final String VEHICLE_REGISTRATION_LICENSE_PLATE_NUMBER_INDEX_NAME = "LicensePlateNumber";
       public static final String DRIVER_LICENSE_NUMBER_INDEX_NAME = "LicenseNumber";
       public static final String DRIVER_LICENSE_PERSONID_INDEX_NAME = "PersonId";
       public static final String JOURNAL_EXPORT_S3_BUCKET_NAME_PREFIX = "qldb-tutorial-journal-export";
       public static final String USER_TABLES = "information_schema.user_tables";
       public static final String LEDGER_NAME_WITH_TAGS = "tags";
       public static final IonSystem SYSTEM = IonSystemBuilder.standard().build();
       public static final IonObjectMapper MAPPER = new IonValueMapper(SYSTEM);
   
       private Constants() { }
   
       static {
           MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
       }
   }
   ```

------
**Nota**  
Questa `Constants` classe include un'istanza della classe Jackson open source. `IonValueMapper` Puoi utilizzare questo mappatore per elaborare i dati di [Amazon Ion](ion.md) durante le transazioni di lettura e scrittura.

   Il `CreateLedger.java` file dipende anche dal seguente programma (`DescribeLedger.java`), che descrive lo stato attuale del registro.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazonaws.services.qldb.AmazonQLDB;
   import com.amazonaws.services.qldb.model.DescribeLedgerRequest;
   import com.amazonaws.services.qldb.model.DescribeLedgerResult;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   /**
    * Describe a QLDB ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class DescribeLedger {
       public static AmazonQLDB client = CreateLedger.getClient();
       public static final Logger log = LoggerFactory.getLogger(DescribeLedger.class);
   
       private DescribeLedger() { }
   
       public static void main(final String... args) {
           try {
   
               describe(Constants.LEDGER_NAME);
   
           } catch (Exception e) {
               log.error("Unable to describe a ledger!", e);
           }
       }
   
       /**
        * Describe a ledger.
        *
        * @param name
        *              Name of the ledger to describe.
        * @return {@link DescribeLedgerResult} from QLDB.
        */
       public static DescribeLedgerResult describe(final String name) {
           log.info("Let's describe ledger with name: {}...", name);
           DescribeLedgerRequest request = new DescribeLedgerRequest().withName(name);
           DescribeLedgerResult result = client.describeLedger(request);
           log.info("Success. Ledger description: {}", result);
           return result;
       }
   }
   ```

1. Compila ed esegui il `CreateLedger.java` programma per creare un registro denominato. `vehicle-registration`

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazonaws.client.builder.AwsClientBuilder;
   import com.amazonaws.services.qldb.AmazonQLDB;
   import com.amazonaws.services.qldb.AmazonQLDBClientBuilder;
   import com.amazonaws.services.qldb.model.CreateLedgerRequest;
   import com.amazonaws.services.qldb.model.CreateLedgerResult;
   import com.amazonaws.services.qldb.model.DescribeLedgerResult;
   import com.amazonaws.services.qldb.model.LedgerState;
   import com.amazonaws.services.qldb.model.PermissionsMode;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   /**
    * Create a ledger and wait for it to be active.
    * <p>
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class CreateLedger {
       public static final Logger log = LoggerFactory.getLogger(CreateLedger.class);
       public static final Long LEDGER_CREATION_POLL_PERIOD_MS = 10_000L;
       public static String endpoint = null;
       public static String region = null;
       public static AmazonQLDB client = getClient();
   
       private CreateLedger() {
       }
   
       /**
        * Build a low-level QLDB client.
        *
        * @return {@link AmazonQLDB} control plane client.
        */
       public static AmazonQLDB getClient() {
           AmazonQLDBClientBuilder builder = AmazonQLDBClientBuilder.standard();
           if (null != endpoint && null != region) {
               builder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region));
           }
           return builder.build();
       }
   
       public static void main(final String... args) throws Exception {
           try {
               client = getClient();
   
               create(Constants.LEDGER_NAME);
   
               waitForActive(Constants.LEDGER_NAME);
   
           } catch (Exception e) {
               log.error("Unable to create the ledger!", e);
               throw e;
           }
       }
   
       /**
        * Create a new ledger with the specified ledger name.
        *
        * @param ledgerName Name of the ledger to be created.
        * @return {@link CreateLedgerResult} from QLDB.
        */
       public static CreateLedgerResult create(final String ledgerName) {
           log.info("Let's create the ledger with name: {}...", ledgerName);
           CreateLedgerRequest request = new CreateLedgerRequest()
                   .withName(ledgerName)
                   .withPermissionsMode(PermissionsMode.ALLOW_ALL);
           CreateLedgerResult result = client.createLedger(request);
           log.info("Success. Ledger state: {}.", result.getState());
           return result;
       }
   
       /**
        * Wait for a newly created ledger to become active.
        *
        * @param ledgerName Name of the ledger to wait on.
        * @return {@link DescribeLedgerResult} from QLDB.
        * @throws InterruptedException if thread is being interrupted.
        */
       public static DescribeLedgerResult waitForActive(final String ledgerName) throws InterruptedException {
           log.info("Waiting for ledger to become active...");
           while (true) {
               DescribeLedgerResult result = DescribeLedger.describe(ledgerName);
               if (result.getState().equals(LedgerState.ACTIVE.name())) {
                   log.info("Success. Ledger is active and ready to use.");
                   return result;
               }
               log.info("The ledger is still creating. Please wait...");
               Thread.sleep(LEDGER_CREATION_POLL_PERIOD_MS);
           }
       }
   }
   ```

------
#### [ 1.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazonaws.services.qldb.AmazonQLDB;
   import com.amazonaws.services.qldb.AmazonQLDBClientBuilder;
   import com.amazonaws.services.qldb.model.CreateLedgerRequest;
   import com.amazonaws.services.qldb.model.CreateLedgerResult;
   import com.amazonaws.services.qldb.model.DescribeLedgerResult;
   import com.amazonaws.services.qldb.model.LedgerState;
   import com.amazonaws.services.qldb.model.PermissionsMode;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   /**
    * Create a ledger and wait for it to be active.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class CreateLedger {
       public static final Logger log = LoggerFactory.getLogger(CreateLedger.class);
       public static final Long LEDGER_CREATION_POLL_PERIOD_MS = 10_000L;
       public static AmazonQLDB client = getClient();
   
       private CreateLedger() { }
   
       /**
        * Build a low-level QLDB client.
        *
        * @return {@link AmazonQLDB} control plane client.
        */
       public static AmazonQLDB getClient() {
           return AmazonQLDBClientBuilder.standard().build();
       }
   
       public static void main(final String... args) throws Exception {
           try {
   
               create(Constants.LEDGER_NAME);
   
               waitForActive(Constants.LEDGER_NAME);
   
           } catch (Exception e) {
               log.error("Unable to create the ledger!", e);
               throw e;
           }
       }
   
       /**
        * Create a new ledger with the specified ledger name.
        *
        * @param ledgerName
        *              Name of the ledger to be created.
        * @return {@link CreateLedgerResult} from QLDB.
        */
       public static CreateLedgerResult create(final String ledgerName) {
           log.info("Let's create the ledger with name: {}...", ledgerName);
           CreateLedgerRequest request = new CreateLedgerRequest()
                   .withName(ledgerName)
                   .withPermissionsMode(PermissionsMode.ALLOW_ALL);
           CreateLedgerResult result = client.createLedger(request);
           log.info("Success. Ledger state: {}.", result.getState());
           return result;
       }
   
       /**
        * Wait for a newly created ledger to become active.
        *
        * @param ledgerName
        *              Name of the ledger to wait on.
        * @return {@link DescribeLedgerResult} from QLDB.
        * @throws InterruptedException if thread is being interrupted.
        */
       public static DescribeLedgerResult waitForActive(final String ledgerName) throws InterruptedException {
           log.info("Waiting for ledger to become active...");
           while (true) {
               DescribeLedgerResult result = DescribeLedger.describe(ledgerName);
               if (result.getState().equals(LedgerState.ACTIVE.name())) {
                   log.info("Success. Ledger is active and ready to use.");
                   return result;
               }
               log.info("The ledger is still creating. Please wait...");
               Thread.sleep(LEDGER_CREATION_POLL_PERIOD_MS);
           }
       }
   }
   ```

------
**Nota**  
Nella `createLedger` chiamata, è necessario specificare un nome di registro e una modalità di autorizzazione. Ti consigliamo di utilizzare la modalità `STANDARD` autorizzazioni per massimizzare la sicurezza dei dati del registro.
Quando si crea un registro, la *protezione da eliminazione* è abilitata per impostazione predefinita. Questa è una funzionalità di QLDB che impedisce l'eliminazione dei registri da parte di qualsiasi utente. È possibile disabilitare la protezione da eliminazione durante la creazione del registro utilizzando l'API QLDB o il (). AWS Command Line Interface AWS CLI
Facoltativamente, puoi anche specificare i tag da allegare al tuo libro mastro.

Per verificare la connessione al nuovo registro, procedi a. [Passaggio 2: verifica la connettività al registro](getting-started.java.step-2.md)

# Passaggio 2: verifica la connettività al registro
Fase 2: Verificare la connettività

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo passaggio, verifichi di poterti connettere al `vehicle-registration` registro in Amazon QLDB utilizzando l'endpoint API dei dati transazionali.

**Per testare la connettività al registro**

1. Esamina il seguente programma (`ConnectToLedger.java`), che crea una connessione della sessione di dati al `vehicle-registration` registro.

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import java.net.URI;
   import java.net.URISyntaxException;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
   import software.amazon.awssdk.services.qldbsession.QldbSessionClient;
   import software.amazon.awssdk.services.qldbsession.QldbSessionClientBuilder;
   import software.amazon.qldb.QldbDriver;
   import software.amazon.qldb.RetryPolicy;
   
   /**
    * Connect to a session for a given ledger using default settings.
    * <p>
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class ConnectToLedger {
       public static final Logger log = LoggerFactory.getLogger(ConnectToLedger.class);
       public static AwsCredentialsProvider credentialsProvider;
       public static String endpoint = null;
       public static String ledgerName = Constants.LEDGER_NAME;
       public static String region = null;
       public static QldbDriver driver;
   
       private ConnectToLedger() {
       }
   
       /**
        * Create a pooled driver for creating sessions.
        *
        * @param retryAttempts How many times the transaction will be retried in
        * case of a retryable issue happens like Optimistic Concurrency Control exception,
        * server side failures or network issues.
        * @return The pooled driver for creating sessions.
        */
       public static QldbDriver createQldbDriver(int retryAttempts) {
           QldbSessionClientBuilder builder = getAmazonQldbSessionClientBuilder();
           return QldbDriver.builder()
                                .ledger(ledgerName)
                                .transactionRetryPolicy(RetryPolicy
                                      .builder()
                                      .maxRetries(retryAttempts)
                                      .build())
                                .sessionClientBuilder(builder)
                                .build();
       }
   
       /**
        * Create a pooled driver for creating sessions.
        *
        * @return The pooled driver for creating sessions.
        */
       public static QldbDriver createQldbDriver() {
           QldbSessionClientBuilder builder = getAmazonQldbSessionClientBuilder();
           return QldbDriver.builder()
               .ledger(ledgerName)
               .transactionRetryPolicy(RetryPolicy.builder()
                                                  .maxRetries(Constants.RETRY_LIMIT).build())
               .sessionClientBuilder(builder)
               .build();
       }
   
       /**
        * Creates a QldbSession builder that is passed to the QldbDriver to connect to the Ledger.
        *
        * @return An instance of the AmazonQLDBSessionClientBuilder
        */
       public static QldbSessionClientBuilder getAmazonQldbSessionClientBuilder() {
           QldbSessionClientBuilder builder = QldbSessionClient.builder();
           if (null != endpoint && null != region) {
               try {
                   builder.endpointOverride(new URI(endpoint));
               } catch (URISyntaxException e) {
                   throw new IllegalArgumentException(e);
               }
           }
           if (null != credentialsProvider) {
               builder.credentialsProvider(credentialsProvider);
           }
           return builder;
       }
   
       /**
        * Create a pooled driver for creating sessions.
        *
        * @return The pooled driver for creating sessions.
        */
       public static QldbDriver getDriver() {
           if (driver == null) {
               driver = createQldbDriver();
           }
           return driver;
       }
   
   
       public static void main(final String... args) {
           Iterable<String> tables = ConnectToLedger.getDriver().getTableNames();
           log.info("Existing tables in the ledger:");
           for (String table : tables) {
               log.info("- {} ", table);
           }
       }
   }
   ```

**Nota**  
Per eseguire operazioni sui dati sul registro, è necessario creare un'istanza della `QldbDriver` classe per connettersi a un registro specifico. Si tratta di un oggetto client diverso dal `AmazonQLDB` client utilizzato nel passaggio precedente per creare il registro. Quel client precedente viene utilizzato solo per eseguire le operazioni dell'API di gestione elencate in. [Riferimento all'API Amazon QLDB](api-reference.md)
Innanzitutto, crea un `QldbDriver` oggetto. È necessario specificare un nome contabile quando si crea questo driver.  
Quindi, è possibile utilizzare il `execute` metodo di questo driver per eseguire istruzioni PartiQL.
Facoltativamente, è possibile specificare un numero massimo di tentativi di nuovo tentativo per le eccezioni delle transazioni. Il `execute` metodo riprova automaticamente i conflitti OCC (Optimistic Concurrency Control) e altre eccezioni transitorie comuni entro questo limite configurabile. Il valore predefinito è `4`.  
Se la transazione continua a fallire dopo il raggiungimento del limite, il driver genera l'eccezione. Per ulteriori informazioni, consulta [Comprendere la politica dei nuovi tentativi con il driver in Amazon QLDB](driver-retry-policy.md).

------
#### [ 1.x ]

   ```
   /*
    * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazonaws.auth.AWSCredentialsProvider;
   import com.amazonaws.client.builder.AwsClientBuilder;
   import com.amazonaws.services.qldbsession.AmazonQLDBSessionClientBuilder;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   import software.amazon.qldb.PooledQldbDriver;
   import software.amazon.qldb.QldbDriver;
   import software.amazon.qldb.QldbSession;
   import software.amazon.qldb.exceptions.QldbClientException;
   
   /**
    * Connect to a session for a given ledger using default settings.
    * <p>
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class ConnectToLedger {
       public static final Logger log = LoggerFactory.getLogger(ConnectToLedger.class);
       public static AWSCredentialsProvider credentialsProvider;
       public static String endpoint = null;
       public static String ledgerName = Constants.LEDGER_NAME;
       public static String region = null;
       private static PooledQldbDriver driver;
   
       private ConnectToLedger() {
       }
   
       /**
        * Create a pooled driver for creating sessions.
        *
        * @return The pooled driver for creating sessions.
        */
       public static PooledQldbDriver createQldbDriver() {
           AmazonQLDBSessionClientBuilder builder = AmazonQLDBSessionClientBuilder.standard();
           if (null != endpoint && null != region) {
               builder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region));
           }
           if (null != credentialsProvider) {
               builder.setCredentials(credentialsProvider);
           }
           return PooledQldbDriver.builder()
                   .withLedger(ledgerName)
                   .withRetryLimit(Constants.RETRY_LIMIT)
                   .withSessionClientBuilder(builder)
                   .build();
       }
   
       /**
        * Create a pooled driver for creating sessions.
        *
        * @return The pooled driver for creating sessions.
        */
       public static PooledQldbDriver getDriver() {
           if (driver == null) {
               driver = createQldbDriver();
           }
           return driver;
       }
   
       /**
        * Connect to a ledger through a {@link QldbDriver}.
        *
        * @return {@link QldbSession}.
        */
       public static QldbSession createQldbSession() {
           return getDriver().getSession();
       }
   
       public static void main(final String... args) {
           try (QldbSession qldbSession = createQldbSession()) {
               log.info("Listing table names ");
               for (String tableName : qldbSession.getTableNames()) {
                   log.info(tableName);
               }
           } catch (QldbClientException e) {
               log.error("Unable to create session.", e);
           }
       }
   }
   ```

**Nota**  
Per eseguire operazioni sui dati sul registro, è necessario creare un'istanza della `QldbDriver` classe `PooledQldbDriver` or per connettersi a un registro specifico. Si tratta di un oggetto client diverso dal `AmazonQLDB` client utilizzato nel passaggio precedente per creare il registro. Quel client precedente viene utilizzato solo per eseguire le operazioni dell'API di gestione elencate in. [Riferimento all'API Amazon QLDB](api-reference.md)  
Si consiglia di utilizzare, `PooledQldbDriver` a meno che non sia necessario implementare un pool di sessioni personalizzato con`QldbDriver`. La dimensione predefinita del pool `PooledQldbDriver` è il [numero massimo di connessioni HTTP aperte](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/ClientConfiguration.html#getMaxConnections--) consentite dal client di sessione.
Innanzitutto, crea un `PooledQldbDriver` oggetto. È necessario specificare un nome contabile quando si crea questo driver.  
Quindi, è possibile utilizzare il `execute` metodo di questo driver per eseguire istruzioni PartiQL. In alternativa, è possibile creare manualmente una sessione da questo oggetto driver in pool e utilizzare il metodo della `execute` sessione. Una sessione rappresenta una singola connessione con il registro.
Facoltativamente, è possibile specificare un numero massimo di tentativi per le eccezioni delle transazioni. Il `execute` metodo riprova automaticamente i conflitti OCC (Optimistic Concurrency Control) e altre eccezioni transitorie comuni entro questo limite configurabile. Il valore predefinito è `4`.  
Se la transazione continua a fallire dopo il raggiungimento del limite, il driver genera l'eccezione. Per ulteriori informazioni, consulta [Comprendere la politica dei nuovi tentativi con il driver in Amazon QLDB](driver-retry-policy.md).

------

1. Compila ed esegui il `ConnectToLedger.java` programma per testare la connettività della sessione di dati al `vehicle-registration` registro.

**Sovrascrivere il Regione AWS**

L'applicazione di esempio si connette a QLDB come Regione AWS impostazione predefinita, che puoi impostare come descritto nel passaggio dei prerequisiti. [Impostazione delle AWS credenziali e della regione predefinite](getting-started.java.md#getting-started.java.credentials) È inoltre possibile modificare la regione modificando le proprietà del generatore del client di sessione QLDB.

------
#### [ 2.x ]

Il seguente esempio di codice crea un'istanza di un nuovo oggetto. `QldbSessionClientBuilder`

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.qldbsession.QldbSessionClientBuilder;

// This client builder will default to US East (Ohio)
QldbSessionClientBuilder builder = QldbSessionClient.builder()
    .region(Region.US_EAST_2);
```

Puoi utilizzare il `region` metodo per eseguire il codice su QLDB in qualsiasi regione in cui è disponibile. Per un elenco completo, consulta gli [endpoint e le quote di Amazon QLDB](https://docs.aws.amazon.com/general/latest/gr/qldb.html) nel. *Riferimenti generali di AWS*

------
#### [ 1.x ]

Il seguente esempio di codice crea un'istanza di un nuovo oggetto. `AmazonQLDBSessionClientBuilder`

```
import com.amazonaws.regions.Regions;
import com.amazonaws.services.qldbsession.AmazonQLDBSessionClientBuilder;

// This client builder will default to US East (Ohio)
AmazonQLDBSessionClientBuilder builder = AmazonQLDBSessionClientBuilder.standard()
    .withRegion(Regions.US_EAST_2);
```

Puoi utilizzare il `withRegion` metodo per eseguire il codice su QLDB in qualsiasi regione in cui è disponibile. Per un elenco completo, consulta gli [endpoint e le quote di Amazon QLDB](https://docs.aws.amazon.com/general/latest/gr/qldb.html) nel. *Riferimenti generali di AWS*

------

Per creare tabelle nel `vehicle-registration` registro, procedi a. [Passaggio 3: Creare tabelle, indici e dati di esempio](getting-started.java.step-3.md)

# Passaggio 3: Creare tabelle, indici e dati di esempio


**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Quando il tuo registro Amazon QLDB è attivo e accetta connessioni, puoi iniziare a creare tabelle per i dati sui veicoli, i loro proprietari e le loro informazioni di immatricolazione. Dopo aver creato le tabelle e gli indici, puoi caricarli con i dati.

In questo passaggio, crei quattro tabelle nel `vehicle-registration` registro:
+ `VehicleRegistration`
+ `Vehicle`
+ `Person`
+ `DriversLicense`

Vengono inoltre creati i seguenti indici.


****  

| Nome tabella | Campo | 
| --- | --- | 
| VehicleRegistration | VIN | 
| VehicleRegistration | LicensePlateNumber | 
| Vehicle | VIN | 
| Person | GovId | 
| DriversLicense | LicenseNumber | 
| DriversLicense | PersonId | 

Quando si inseriscono dati di esempio, si inseriscono innanzitutto i documenti nella tabella. `Person` Quindi, si utilizzano i dati assegnati `id` dal sistema a ciascun `Person` documento per compilare i campi corrispondenti nei documenti e nei documenti appropriati. `VehicleRegistration` `DriversLicense`

**Suggerimento**  
Come procedura ottimale, utilizzate il sistema assegnato a un documento come chiave esterna`id`. Sebbene sia possibile definire campi destinati a essere identificatori univoci (ad esempio, il VIN di un veicolo), il vero identificatore univoco di un documento è il suo. `id` Questo campo è incluso nei metadati del documento, a cui è possibile eseguire una query nella visualizzazione confermata (*la vista* di una tabella definita dal sistema).  
Per ulteriori informazioni sulle viste in QLDB, vedere. [Concetti principali](ledger-structure.md) Per ulteriori informazioni sui metadati, consulta. [Interrogazione dei metadati dei documenti](working.metadata.md)

**Per configurare i dati di esempio**

1. Esamina i seguenti `.java` file. Queste classi di modelli rappresentano i documenti archiviati nelle `vehicle-registration` tabelle. Sono serializzabili da e verso il formato Amazon Ion.
**Nota**  
[Documenti Amazon QLDB](ql-reference.docs.md)sono archiviati in formato Ion, che è un superset di JSON. Quindi, puoi usare la libreria FasterXML Jackson per modellare i dati in JSON.

   1. `DriversLicense.java`

      ```
      /*
       * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.model;
      
      import com.fasterxml.jackson.annotation.JsonCreator;
      import com.fasterxml.jackson.annotation.JsonProperty;
      import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
      import com.fasterxml.jackson.databind.annotation.JsonSerialize;
      import software.amazon.qldb.tutorial.model.streams.RevisionData;
      
      import java.time.LocalDate;
      
      /**
       * Represents a driver's license, serializable to (and from) Ion.
       */
      public final class DriversLicense implements RevisionData {
          private final String personId;
          private final String licenseNumber;
          private final String licenseType;
      
          @JsonSerialize(using = IonLocalDateSerializer.class)
          @JsonDeserialize(using = IonLocalDateDeserializer.class)
          private final LocalDate validFromDate;
      
          @JsonSerialize(using = IonLocalDateSerializer.class)
          @JsonDeserialize(using = IonLocalDateDeserializer.class)
          private final LocalDate validToDate;
      
          @JsonCreator
          public DriversLicense(@JsonProperty("PersonId") final String personId,
                                @JsonProperty("LicenseNumber") final String licenseNumber,
                                @JsonProperty("LicenseType") final String licenseType,
                                @JsonProperty("ValidFromDate") final LocalDate validFromDate,
                                @JsonProperty("ValidToDate") final LocalDate validToDate) {
              this.personId = personId;
              this.licenseNumber = licenseNumber;
              this.licenseType = licenseType;
              this.validFromDate = validFromDate;
              this.validToDate = validToDate;
          }
      
          @JsonProperty("PersonId")
          public String getPersonId() {
              return personId;
          }
      
          @JsonProperty("LicenseNumber")
          public String getLicenseNumber() {
              return licenseNumber;
          }
      
          @JsonProperty("LicenseType")
          public String getLicenseType() {
              return licenseType;
          }
      
          @JsonProperty("ValidFromDate")
          public LocalDate getValidFromDate() {
              return  validFromDate;
          }
      
          @JsonProperty("ValidToDate")
          public LocalDate getValidToDate() {
              return  validToDate;
          }
      
          @Override
          public String toString() {
              return "DriversLicense{" +
                      "personId='" + personId + '\'' +
                      ", licenseNumber='" + licenseNumber + '\'' +
                      ", licenseType='" + licenseType + '\'' +
                      ", validFromDate=" + validFromDate +
                      ", validToDate=" + validToDate +
                      '}';
          }
      }
      ```

   1. `Person.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.model;
      
      import java.time.LocalDate;
      
      import com.fasterxml.jackson.annotation.JsonCreator;
      import com.fasterxml.jackson.annotation.JsonProperty;
      import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
      import com.fasterxml.jackson.databind.annotation.JsonSerialize;
      
      import software.amazon.qldb.TransactionExecutor;
      import software.amazon.qldb.tutorial.Constants;
      import software.amazon.qldb.tutorial.model.streams.RevisionData;
      
      /**
       * Represents a person, serializable to (and from) Ion.
       */ 
      public final class Person implements RevisionData {
          private final String firstName;
          private final String lastName;
      
          @JsonSerialize(using = IonLocalDateSerializer.class)
          @JsonDeserialize(using = IonLocalDateDeserializer.class)
          private final LocalDate dob;
          private final String govId;
          private final String govIdType;
          private final String address;
      
          @JsonCreator
          public Person(@JsonProperty("FirstName") final String firstName,
                        @JsonProperty("LastName") final String lastName,
                        @JsonProperty("DOB") final LocalDate dob,
                        @JsonProperty("GovId") final String govId,
                        @JsonProperty("GovIdType") final String govIdType,
                        @JsonProperty("Address") final String address) {
              this.firstName = firstName;
              this.lastName = lastName;
              this.dob = dob;
              this.govId = govId;
              this.govIdType = govIdType;
              this.address = address;
          }
      
          @JsonProperty("Address")
          public String getAddress() {
              return address;
          }
      
          @JsonProperty("DOB")
          public LocalDate getDob() {
              return dob;
          }
      
          @JsonProperty("FirstName")
          public String getFirstName() {
              return firstName;
          }
      
          @JsonProperty("LastName")
          public String getLastName() {
              return lastName;
          }
      
          @JsonProperty("GovId")
          public String getGovId() {
              return govId;
          }
      
          @JsonProperty("GovIdType")
          public String getGovIdType() {
              return govIdType;
          }
      
          /**
           * This returns the unique document ID given a specific government ID.
           *
           * @param txn
           *              A transaction executor object.
           * @param govId
           *              The government ID of a driver.
           * @return the unique document ID.
           */
          public static String getDocumentIdByGovId(final TransactionExecutor txn, final String govId) {
              return SampleData.getDocumentId(txn, Constants.PERSON_TABLE_NAME, "GovId", govId);
          }
      
          @Override
          public String toString() {
              return "Person{" +
                      "firstName='" + firstName + '\'' +
                      ", lastName='" + lastName + '\'' +
                      ", dob=" + dob +
                      ", govId='" + govId + '\'' +
                      ", govIdType='" + govIdType + '\'' +
                      ", address='" + address + '\'' +
                      '}';
          }
      }
      ```

   1. `VehicleRegistration.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.model;
      
      import com.fasterxml.jackson.annotation.JsonCreator;
      import com.fasterxml.jackson.annotation.JsonProperty;
      import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
      import com.fasterxml.jackson.databind.annotation.JsonSerialize;
      import software.amazon.qldb.TransactionExecutor;
      import software.amazon.qldb.tutorial.Constants;
      import software.amazon.qldb.tutorial.model.streams.RevisionData;
      
      import java.math.BigDecimal;
      import java.time.LocalDate;
      
      /**
       * Represents a vehicle registration, serializable to (and from) Ion.
       */ 
      public final class VehicleRegistration implements RevisionData {
      
          private final String vin;
          private final String licensePlateNumber;
          private final String state;
          private final String city;
          private final BigDecimal pendingPenaltyTicketAmount;
          private final LocalDate validFromDate;
          private final LocalDate validToDate;
          private final Owners owners;
      
          @JsonCreator
          public VehicleRegistration(@JsonProperty("VIN") final String vin,
                                     @JsonProperty("LicensePlateNumber") final String licensePlateNumber,
                                     @JsonProperty("State") final String state,
                                     @JsonProperty("City") final String city,
                                     @JsonProperty("PendingPenaltyTicketAmount") final BigDecimal pendingPenaltyTicketAmount,
                                     @JsonProperty("ValidFromDate") final LocalDate validFromDate,
                                     @JsonProperty("ValidToDate") final LocalDate validToDate,
                                     @JsonProperty("Owners") final Owners owners) {
              this.vin = vin;
              this.licensePlateNumber = licensePlateNumber;
              this.state = state;
              this.city = city;
              this.pendingPenaltyTicketAmount = pendingPenaltyTicketAmount;
              this.validFromDate = validFromDate;
              this.validToDate = validToDate;
              this.owners = owners;
          }
      
          @JsonProperty("City")
          public String getCity() {
              return city;
          }
      
          @JsonProperty("LicensePlateNumber")
          public String getLicensePlateNumber() {
              return licensePlateNumber;
          }
      
          @JsonProperty("Owners")
          public Owners getOwners() {
              return owners;
          }
      
          @JsonProperty("PendingPenaltyTicketAmount")
          public BigDecimal getPendingPenaltyTicketAmount() {
              return pendingPenaltyTicketAmount;
          }
      
          @JsonProperty("State")
          public String getState() {
              return state;
          }
      
          @JsonProperty("ValidFromDate")
          @JsonSerialize(using = IonLocalDateSerializer.class)
          @JsonDeserialize(using = IonLocalDateDeserializer.class)
          public LocalDate getValidFromDate() {
              return validFromDate;
          }
      
          @JsonProperty("ValidToDate")
          @JsonSerialize(using = IonLocalDateSerializer.class)
          @JsonDeserialize(using = IonLocalDateDeserializer.class)
          public LocalDate getValidToDate() {
              return validToDate;
          }
      
          @JsonProperty("VIN")
          public String getVin() {
              return vin;
          }
      
          /**
           * Returns the unique document ID of a vehicle given a specific VIN.
           *
           * @param txn
           *              A transaction executor object.
           * @param vin
           *              The VIN of a vehicle.
           * @return the unique document ID of the specified vehicle.
           */
          public static String getDocumentIdByVin(final TransactionExecutor txn, final String vin) {
              return SampleData.getDocumentId(txn, Constants.VEHICLE_REGISTRATION_TABLE_NAME, "VIN", vin);
          }
      
          @Override
          public String toString() {
              return "VehicleRegistration{" +
                      "vin='" + vin + '\'' +
                      ", licensePlateNumber='" + licensePlateNumber + '\'' +
                      ", state='" + state + '\'' +
                      ", city='" + city + '\'' +
                      ", pendingPenaltyTicketAmount=" + pendingPenaltyTicketAmount +
                      ", validFromDate=" + validFromDate +
                      ", validToDate=" + validToDate +
                      ", owners=" + owners +
                      '}';
          }
      }
      ```

   1. `Vehicle.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.model;
      
      import com.fasterxml.jackson.annotation.JsonCreator;
      import com.fasterxml.jackson.annotation.JsonProperty;
      import software.amazon.qldb.tutorial.model.streams.RevisionData;
      
      /**
       * Represents a vehicle, serializable to (and from) Ion.
       */
      public final class Vehicle implements RevisionData {
          private final String vin;
          private final String type;
          private final int year;
          private final String make;
          private final String model;
          private final String color;
      
          @JsonCreator
          public Vehicle(@JsonProperty("VIN") final String vin,
                         @JsonProperty("Type") final String type,
                         @JsonProperty("Year") final int year,
                         @JsonProperty("Make") final String make,
                         @JsonProperty("Model") final String model,
                         @JsonProperty("Color") final String color) {
              this.vin = vin;
              this.type = type;
              this.year = year;
              this.make = make;
              this.model = model;
              this.color = color;
          }
      
          @JsonProperty("Color")
          public String getColor() {
              return color;
          }
      
          @JsonProperty("Make")
          public String getMake() {
              return make;
          }
      
          @JsonProperty("Model")
          public String getModel() {
              return model;
          }
      
          @JsonProperty("Type")
          public String getType() {
              return type;
          }
      
          @JsonProperty("VIN")
          public String getVin() {
              return vin;
          }
      
          @JsonProperty("Year")
          public int getYear() {
              return year;
          }
      
          @Override
          public String toString() {
              return "Vehicle{" +
                      "vin='" + vin + '\'' +
                      ", type='" + type + '\'' +
                      ", year=" + year +
                      ", make='" + make + '\'' +
                      ", model='" + model + '\'' +
                      ", color='" + color + '\'' +
                      '}';
          }
      }
      ```

   1. `Owner.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.model;
      
      import com.fasterxml.jackson.annotation.JsonProperty;
      
      /**
       * Represents a vehicle owner, serializable to (and from) Ion.
       */ 
      public final class Owner {
          private final String personId;
      
          public Owner(@JsonProperty("PersonId") final String personId) {
              this.personId = personId;
          }
      
          @JsonProperty("PersonId")
          public String getPersonId() {
              return personId;
          }
      
          @Override
          public String toString() {
              return "Owner{" +
                      "personId='" + personId + '\'' +
                      '}';
          }
      }
      ```

   1. `Owners.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.model;
      
      import com.fasterxml.jackson.annotation.JsonProperty;
      
      import java.util.List;
      
      /**
       * Represents a set of owners for a given vehicle, serializable to (and from) Ion.
       */ 
      public final class Owners {
          private final Owner primaryOwner;
          private final List<Owner> secondaryOwners;
      
          public Owners(@JsonProperty("PrimaryOwner") final Owner primaryOwner,
                        @JsonProperty("SecondaryOwners") final List<Owner> secondaryOwners) {
              this.primaryOwner = primaryOwner;
              this.secondaryOwners = secondaryOwners;
          }
      
          @JsonProperty("PrimaryOwner")
          public Owner getPrimaryOwner() {
              return primaryOwner;
          }
      
          @JsonProperty("SecondaryOwners")
          public List<Owner> getSecondaryOwners() {
              return secondaryOwners;
          }
      
          @Override
          public String toString() {
              return "Owners{" +
                      "primaryOwner=" + primaryOwner +
                      ", secondaryOwners=" + secondaryOwners +
                      '}';
          }
      }
      ```

   1. `DmlResultDocument.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.qldb;
      
      import com.fasterxml.jackson.annotation.JsonCreator;
      import com.fasterxml.jackson.annotation.JsonProperty;
      
      /**
       * Contains information about an individual document inserted or modified
       * as a result of DML.
       */
      public class DmlResultDocument {
      
          private String documentId;
      
          @JsonCreator
          public DmlResultDocument(@JsonProperty("documentId") final String documentId) {
              this.documentId = documentId;
          }
      
          public String getDocumentId() {
              return documentId;
          }
      
          @Override
          public String toString() {
              return "DmlResultDocument{"
                  + "documentId='" + documentId + '\''
                  + '}';
          }
      }
      ```

   1. `RevisionData.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.model.streams;
      
      /**
       * Allows modeling the content of all revisions as a generic revision data. Used
       * in the {@link Revision} and extended by domain models in {@link
       * software.amazon.qldb.tutorial.model} to make it easier to write the {@link
       * Revision.RevisionDataDeserializer} that must deserialize the {@link
       * Revision#data} from different domain models.
       */
      public interface RevisionData { }
      ```

   1. `RevisionMetadata.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.qldb;
      
      import com.amazon.ion.IonInt;
      import com.amazon.ion.IonString;
      import com.amazon.ion.IonStruct;
      import com.amazon.ion.IonTimestamp;
      import com.fasterxml.jackson.annotation.JsonCreator;
      import com.fasterxml.jackson.annotation.JsonProperty;
      import com.fasterxml.jackson.databind.annotation.JsonSerialize;
      import com.fasterxml.jackson.dataformat.ion.IonTimestampSerializers;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      import java.util.Date;
      import java.util.Objects;
      
      /**
       * Represents the metadata field of a QLDB Document
       */
      public class RevisionMetadata {
          private static final Logger log = LoggerFactory.getLogger(RevisionMetadata.class);
          private final String id;
          private final long version;
          @JsonSerialize(using = IonTimestampSerializers.IonTimestampJavaDateSerializer.class)
          private final Date txTime;
          private final String txId;
      
          @JsonCreator
          public RevisionMetadata(@JsonProperty("id") final String id,
                                  @JsonProperty("version") final long version,
                                  @JsonProperty("txTime") final Date txTime,
                                  @JsonProperty("txId") final String txId) {
              this.id = id;
              this.version = version;
              this.txTime = txTime;
              this.txId = txId;
          }
      
          /**
           * Gets the unique ID of a QLDB document.
           *
           * @return the document ID.
           */
          public String getId() {
              return id;
          }
      
          /**
           * Gets the version number of the document in the document's modification history.
           * @return the version number.
           */
          public long getVersion() {
              return version;
          }
      
          /**
           * Gets the time during which the document was modified.
           *
           * @return the transaction time.
           */
          public Date getTxTime() {
              return txTime;
          }
      
          /**
           * Gets the transaction ID associated with this document.
           *
           * @return the transaction ID.
           */
          public String getTxId() {
              return txId;
          }
      
          public static RevisionMetadata fromIon(final IonStruct ionStruct) {
              if (ionStruct == null) {
                  throw new IllegalArgumentException("Metadata cannot be null");
              }
              try {
                  IonString id = (IonString) ionStruct.get("id");
                  IonInt version = (IonInt) ionStruct.get("version");
                  IonTimestamp txTime = (IonTimestamp) ionStruct.get("txTime");
                  IonString txId = (IonString) ionStruct.get("txId");
                  if (id == null || version == null || txTime == null || txId == null) {
                      throw new IllegalArgumentException("Document is missing required fields");
                  }
                  return new RevisionMetadata(id.stringValue(), version.longValue(), new Date(txTime.getMillis()), txId.stringValue());
              } catch (ClassCastException e) {
                  log.error("Failed to parse ion document");
                  throw new IllegalArgumentException("Document members are not of the correct type", e);
              }
          }
      
          /**
           * Converts a {@link RevisionMetadata} object to a string.
           *
           * @return the string representation of the {@link QldbRevision} object.
           */
          @Override
          public String toString() {
              return "Metadata{"
                      + "id='" + id + '\''
                      + ", version=" + version
                      + ", txTime=" + txTime
                      + ", txId='" + txId
                      + '\''
                      + '}';
          }
      
          /**
           * Check whether two {@link RevisionMetadata} objects are equivalent.
           *
           * @return {@code true} if the two objects are equal, {@code false} otherwise.
           */
          @Override
          public boolean equals(Object o) {
              if (this == o) { return true; }
              if (o == null || getClass() != o.getClass()) { return false; }
              RevisionMetadata metadata = (RevisionMetadata) o;
              return version == metadata.version
                      && id.equals(metadata.id)
                      && txTime.equals(metadata.txTime)
                      && txId.equals(metadata.txId);
          }
      
          /**
           * Generate a hash code for the {@link RevisionMetadata} object.
           *
           * @return the hash code.
           */
          @Override
          public int hashCode() {
              // CHECKSTYLE:OFF - Disabling as we are generating a hashCode of multiple properties.
              return Objects.hash(id, version, txTime, txId);
              // CHECKSTYLE:ON
          }
      }
      ```

   1. `QldbRevision.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.qldb;
      
      import com.amazon.ion.IonBlob;
      import com.amazon.ion.IonStruct;
      import com.fasterxml.jackson.annotation.JsonCreator;
      import com.fasterxml.jackson.annotation.JsonProperty;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import software.amazon.qldb.tutorial.Constants;
      import software.amazon.qldb.tutorial.Verifier;
      
      import java.io.IOException;
      import java.util.Arrays;
      import java.util.Objects;
      
      /**
       * Represents a QldbRevision including both user data and metadata.
       */
      public final class QldbRevision {
          private static final Logger log = LoggerFactory.getLogger(QldbRevision.class);
      
          private final BlockAddress blockAddress;
          private final RevisionMetadata metadata;
          private final byte[] hash;
          private final byte[] dataHash;
          private final IonStruct data;
      
          @JsonCreator
          public QldbRevision(@JsonProperty("blockAddress") final BlockAddress blockAddress,
                              @JsonProperty("metadata") final RevisionMetadata metadata,
                              @JsonProperty("hash") final byte[] hash,
                              @JsonProperty("dataHash") final byte[] dataHash,
                              @JsonProperty("data") final IonStruct data) {
              this.blockAddress = blockAddress;
              this.metadata = metadata;
              this.hash = hash;
              this.dataHash = dataHash;
              this.data = data;
          }
      
          /**
           * Gets the unique ID of a QLDB document.
           *
           * @return the {@link BlockAddress} object.
           */
          public BlockAddress getBlockAddress() {
              return blockAddress;
          }
      
          /**
           * Gets the metadata of the revision.
           *
           * @return the {@link RevisionMetadata} object.
           */
          public RevisionMetadata getMetadata() {
              return metadata;
          }
      
          /**
           * Gets the SHA-256 hash value of the revision.
           * This is equivalent to the hash of the revision metadata and data.
           *
           * @return the byte array representing the hash.
           */
          public byte[] getHash() {
              return hash;
          }
      
          /**
           * Gets the SHA-256 hash value of the data portion of the revision.
           * This is only present if the revision is redacted.
           *
           * @return the byte array representing the hash.
           */
          public byte[] getDataHash() {
              return dataHash;
          }
      
          /**
           * Gets the revision data.
           *
           * @return the revision data.
           */
          public IonStruct getData() {
              return data;
          }
      
          /**
           * Returns true if the revision has been redacted.
           * @return a boolean value representing the redaction status
           * of this revision.
           */
          public Boolean isRedacted() {
              return dataHash != null;
          }
      
          /**
           * Constructs a new {@link QldbRevision} from an {@link IonStruct}.
           *
           * The specified {@link IonStruct} must include the following fields
           *
           * - blockAddress -- a {@link BlockAddress},
           * - metadata -- a {@link RevisionMetadata},
           * - hash -- the revision's hash calculated by QLDB,
           * - dataHash -- the user data's hash calculated by QLDB (only present if revision is redacted),
           * - data -- an {@link IonStruct} containing user data in the document.
           *
           * If any of these fields are missing or are malformed, then throws {@link IllegalArgumentException}.
           *
           * If the document hash calculated from the members of the specified {@link IonStruct} does not match
           * the hash member of the {@link IonStruct} then throws {@link IllegalArgumentException}.
           *
           * @param ionStruct
           *              The {@link IonStruct} that contains a {@link QldbRevision} object.
           * @return the converted {@link QldbRevision} object.
           * @throws IOException if failed to parse parameter {@link IonStruct}.
           */
          public static QldbRevision fromIon(final IonStruct ionStruct) throws IOException {
              try {
                  BlockAddress blockAddress = Constants.MAPPER.readValue(ionStruct.get("blockAddress"), BlockAddress.class);
                  IonBlob revisionHash = (IonBlob) ionStruct.get("hash");
                  IonStruct metadataStruct = (IonStruct) ionStruct.get("metadata");
                  IonStruct data = ionStruct.get("data") == null || ionStruct.get("data").isNullValue() ?
                      null : (IonStruct) ionStruct.get("data");
                  IonBlob dataHash = ionStruct.get("dataHash") == null || ionStruct.get("dataHash").isNullValue() ?
                      null : (IonBlob) ionStruct.get("dataHash");
                  if (revisionHash == null || metadataStruct == null) {
                      throw new IllegalArgumentException("Document is missing required fields");
                  }
                  byte[] dataHashBytes = dataHash != null ? dataHash.getBytes() : QldbIonUtils.hashIonValue(data);
                  verifyRevisionHash(metadataStruct, dataHashBytes, revisionHash.getBytes());
                  RevisionMetadata metadata = RevisionMetadata.fromIon(metadataStruct);
                  return new QldbRevision(
                          blockAddress,
                          metadata,
                          revisionHash.getBytes(),
                          dataHash != null ? dataHash.getBytes() : null,
                          data
                  );
              } catch (ClassCastException e) {
                  log.error("Failed to parse ion document");
                  throw new IllegalArgumentException("Document members are not of the correct type", e);
              }
          }
      
          /**
           * Converts a {@link QldbRevision} object to string.
           *
           * @return the string representation of the {@link QldbRevision} object.
           */
          @Override
          public String toString() {
              return "QldbRevision{" +
                      "blockAddress=" + blockAddress +
                      ", metadata=" + metadata +
                      ", hash=" + Arrays.toString(hash) +
                      ", dataHash=" + Arrays.toString(dataHash) +
                      ", data=" + data +
                      '}';
          }
      
          /**
           * Check whether two {@link QldbRevision} objects are equivalent.
           *
           * @return {@code true} if the two objects are equal, {@code false} otherwise.
           */
          @Override
          public boolean equals(final Object o) {
              if (this == o) {
                  return true;
              }
              if (!(o instanceof QldbRevision)) {
                  return false;
              }
              final QldbRevision that = (QldbRevision) o;
              return Objects.equals(getBlockAddress(), that.getBlockAddress())
                      && Objects.equals(getMetadata(), that.getMetadata())
                      && Arrays.equals(getHash(), that.getHash())
                      && Arrays.equals(getDataHash(), that.getDataHash())
                      && Objects.equals(getData(), that.getData());
          }
      
          /**
           * Create a hash code for the {@link QldbRevision} object.
           *
           * @return the hash code.
           */
          @Override
          public int hashCode() {
              // CHECKSTYLE:OFF - Disabling as we are generating a hashCode of multiple properties.
              int result = Objects.hash(blockAddress, metadata, data);
              // CHECKSTYLE:ON
              result = 31 * result + Arrays.hashCode(hash);
              return result;
          }
      
          /**
           * Throws an IllegalArgumentException if the hash of the revision data and metadata
           * does not match the hash provided by QLDB with the revision.
           */
          public void verifyRevisionHash() {
              // Certain internal-only system revisions only contain a hash which cannot be
              // further computed. However, these system hashes still participate to validate
              // the journal block. User revisions will always contain values for all fields
              // and can therefore have their hash computed.
              if (blockAddress == null && metadata == null && data == null && dataHash == null) {
                  return;
              }
      
              try {
                  IonStruct metadataIon = (IonStruct) Constants.MAPPER.writeValueAsIonValue(metadata);
                  byte[] dataHashBytes = isRedacted() ? dataHash : QldbIonUtils.hashIonValue(data);
                  verifyRevisionHash(metadataIon, dataHashBytes, hash);
              } catch (IOException e) {
                  throw new IllegalArgumentException("Could not encode revision metadata to ion.", e);
              }
          }
      
          private static void verifyRevisionHash(IonStruct metadata, byte[] dataHash, byte[] expectedHash) {
              byte[] metadataHash = QldbIonUtils.hashIonValue(metadata);
              byte[] candidateHash = Verifier.dot(metadataHash, dataHash);
              if (!Arrays.equals(candidateHash, expectedHash)) {
                  throw new IllegalArgumentException("Hash entry of QLDB revision and computed hash "
                          + "of QLDB revision do not match");
              }
          }
      }
      ```

   1. `IonLocalDateDeserializer.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.model;
      
      import com.amazon.ion.Timestamp;
      import com.fasterxml.jackson.core.JsonParser;
      import com.fasterxml.jackson.databind.DeserializationContext;
      import com.fasterxml.jackson.databind.JsonDeserializer;
      
      import java.io.IOException;
      import java.time.LocalDate;
      
      /**
       * Deserializes [java.time.LocalDate] from Ion.
       */
      public class IonLocalDateDeserializer extends JsonDeserializer<LocalDate> {
      
          @Override
          public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
              return timestampToLocalDate((Timestamp) jp.getEmbeddedObject());
          }
      
          private LocalDate timestampToLocalDate(Timestamp timestamp) {
              return LocalDate.of(timestamp.getYear(), timestamp.getMonth(), timestamp.getDay());
          }
      }
      ```

   1. `IonLocalDateSerializer.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.model;
      
      import com.amazon.ion.Timestamp;
      import com.fasterxml.jackson.core.JsonGenerator;
      import com.fasterxml.jackson.databind.SerializerProvider;
      import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
      import com.fasterxml.jackson.dataformat.ion.IonGenerator;
      
      import java.io.IOException;
      import java.time.LocalDate;
      
      /**
       * Serializes [java.time.LocalDate] to Ion.
       */
      public class IonLocalDateSerializer extends StdScalarSerializer<LocalDate> {
      
          public IonLocalDateSerializer() {
              super(LocalDate.class);
          }
      
          @Override
          public void serialize(LocalDate date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
              Timestamp timestamp = Timestamp.forDay(date.getYear(), date.getMonthValue(), date.getDayOfMonth());
              ((IonGenerator) jsonGenerator).writeValue(timestamp);
          }
      }
      ```

1. Esaminate il seguente file (`SampleData.java`), che rappresenta i dati di esempio che inserite nelle tabelle. `vehicle-registration`

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial.model;
   
   import com.amazon.ion.IonString;
   import com.amazon.ion.IonStruct;
   import com.amazon.ion.IonValue;
   import java.io.IOException;
   import java.math.BigDecimal;
   import java.text.ParseException;
   import java.time.LocalDate;
   import java.time.format.DateTimeFormatter;
   import java.util.ArrayList;
   import java.util.Arrays;
   import java.util.Collections;
   import java.util.List;
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.ConnectToLedger;
   import software.amazon.qldb.tutorial.Constants;
   import software.amazon.qldb.tutorial.qldb.DmlResultDocument;
   import software.amazon.qldb.tutorial.qldb.QldbRevision;
   
   /**
    * Sample domain objects for use throughout this tutorial.
    */
   public final class SampleData {
       public static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
   
       public static final List<VehicleRegistration> REGISTRATIONS = Collections.unmodifiableList(Arrays.asList(
               new VehicleRegistration("1N4AL11D75C109151", "LEWISR261LL", "WA", "Seattle",
                       BigDecimal.valueOf(90.25), convertToLocalDate("2017-08-21"), convertToLocalDate("2020-05-11"),
                       new Owners(new Owner(null), Collections.emptyList())),
               new VehicleRegistration("KM8SRDHF6EU074761", "CA762X", "WA", "Kent",
                       BigDecimal.valueOf(130.75), convertToLocalDate("2017-09-14"), convertToLocalDate("2020-06-25"),
                       new Owners(new Owner(null), Collections.emptyList())),
               new VehicleRegistration("3HGGK5G53FM761765", "CD820Z", "WA", "Everett",
                       BigDecimal.valueOf(442.30), convertToLocalDate("2011-03-17"), convertToLocalDate("2021-03-24"),
                       new Owners(new Owner(null), Collections.emptyList())),
               new VehicleRegistration("1HVBBAANXWH544237", "LS477D", "WA", "Tacoma",
                       BigDecimal.valueOf(42.20), convertToLocalDate("2011-10-26"), convertToLocalDate("2023-09-25"),
                       new Owners(new Owner(null), Collections.emptyList())),
               new VehicleRegistration("1C4RJFAG0FC625797", "TH393F", "WA", "Olympia",
                       BigDecimal.valueOf(30.45), convertToLocalDate("2013-09-02"), convertToLocalDate("2024-03-19"),
                       new Owners(new Owner(null), Collections.emptyList()))
       ));
   
       public static final List<Vehicle> VEHICLES = Collections.unmodifiableList(Arrays.asList(
               new Vehicle("1N4AL11D75C109151", "Sedan", 2011,  "Audi", "A5", "Silver"),
               new Vehicle("KM8SRDHF6EU074761", "Sedan", 2015, "Tesla", "Model S", "Blue"),
               new Vehicle("3HGGK5G53FM761765", "Motorcycle", 2011, "Ducati", "Monster 1200", "Yellow"),
               new Vehicle("1HVBBAANXWH544237", "Semi", 2009, "Ford", "F 150", "Black"),
               new Vehicle("1C4RJFAG0FC625797", "Sedan", 2019, "Mercedes", "CLK 350", "White")
       ));
   
       public static final List<Person> PEOPLE = Collections.unmodifiableList(Arrays.asList(
               new Person("Raul", "Lewis", convertToLocalDate("1963-08-19"),
                       "LEWISR261LL", "Driver License",  "1719 University Street, Seattle, WA, 98109"),
               new Person("Brent", "Logan", convertToLocalDate("1967-07-03"),
                       "LOGANB486CG", "Driver License", "43 Stockert Hollow Road, Everett, WA, 98203"),
               new Person("Alexis", "Pena", convertToLocalDate("1974-02-10"),
                       "744 849 301", "SSN", "4058 Melrose Street, Spokane Valley, WA, 99206"),
               new Person("Melvin", "Parker", convertToLocalDate("1976-05-22"),
                       "P626-168-229-765", "Passport", "4362 Ryder Avenue, Seattle, WA, 98101"),
               new Person("Salvatore", "Spencer", convertToLocalDate("1997-11-15"),
                       "S152-780-97-415-0", "Passport", "4450 Honeysuckle Lane, Seattle, WA, 98101")
       ));
   
       public static final List<DriversLicense> LICENSES = Collections.unmodifiableList(Arrays.asList(
               new DriversLicense(null, "LEWISR261LL", "Learner",
                       convertToLocalDate("2016-12-20"), convertToLocalDate("2020-11-15")),
               new DriversLicense(null, "LOGANB486CG", "Probationary",
                       convertToLocalDate("2016-04-06"), convertToLocalDate("2020-11-15")),
               new DriversLicense(null, "744 849 301", "Full",
                       convertToLocalDate("2017-12-06"), convertToLocalDate("2022-10-15")),
               new DriversLicense(null, "P626-168-229-765", "Learner",
                       convertToLocalDate("2017-08-16"), convertToLocalDate("2021-11-15")),
               new DriversLicense(null, "S152-780-97-415-0", "Probationary",
                       convertToLocalDate("2015-08-15"), convertToLocalDate("2021-08-21"))
       ));
   
       private SampleData() { }
   
       /**
        * Converts a date string with the format 'yyyy-MM-dd' into a {@link java.util.Date} object.
        *
        * @param date
        *              The date string to convert.
        * @return {@link java.time.LocalDate} or null if there is a {@link ParseException}
        */
       public static synchronized LocalDate convertToLocalDate(String date) {
           return LocalDate.parse(date, DATE_TIME_FORMAT);
       }
   
       /**
        * Convert the result set into a list of IonValues.
        *
        * @param result
        *              The result set to convert.
        * @return a list of IonValues.
        */
       public static List<IonValue> toIonValues(Result result) {
           final List<IonValue> valueList = new ArrayList<>();
           result.iterator().forEachRemaining(valueList::add);
           return valueList;
       }
   
       /**
        * Get the document ID of a particular document.
        *
        * @param txn
        *              A transaction executor object.
        * @param tableName
        *              Name of the table containing the document.
        * @param identifier
        *              The identifier used to narrow down the search.
        * @param value
        *              Value of the identifier.
        * @return the list of document IDs in the result set.
        */
       public static String getDocumentId(final TransactionExecutor txn, final String tableName,
                                          final String identifier, final String value) {
           try {
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(value));
               final String query = String.format("SELECT metadata.id FROM _ql_committed_%s AS p WHERE p.data.%s = ?",
                       tableName, identifier);
               Result result = txn.execute(query, parameters);
               if (result.isEmpty()) {
                   throw new IllegalStateException("Unable to retrieve document ID using " + value);
               }
               return getStringValueOfStructField((IonStruct) result.iterator().next(), "id");
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Get the document by ID.
        *
        * @param tableName
        *              Name of the table to insert documents into.
        * @param documentId
        *              The unique ID of a document in the Person table.
        * @return a {@link QldbRevision} object.
        * @throws IllegalStateException if failed to convert parameter into {@link IonValue}.
        */
       public static QldbRevision getDocumentById(String tableName, String documentId) {
           try {
               final IonValue ionValue = Constants.MAPPER.writeValueAsIonValue(documentId);
               Result result = ConnectToLedger.getDriver().execute(txn -> {
                   return txn.execute("SELECT c.* FROM _ql_committed_" + tableName + " AS c BY docId "
                                      + "WHERE docId = ?", ionValue);
               });
               if (result.isEmpty()) {
                   throw new IllegalStateException("Unable to retrieve document by id " + documentId + " in table " + tableName);
               }
               return Constants.MAPPER.readValue(result.iterator().next(), QldbRevision.class);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Return a list of modified document IDs as strings from a DML {@link Result}.
        *
        * @param result
        *              The result set from a DML operation.
        * @return the list of document IDs modified by the operation.
        */
       public static List<String> getDocumentIdsFromDmlResult(final Result result) {
           final List<String> strings = new ArrayList<>();
           result.iterator().forEachRemaining(row -> strings.add(getDocumentIdFromDmlResultDocument(row)));
           return strings;
       }
   
       /**
        * Convert the given DML result row's document ID to string.
        *
        * @param dmlResultDocument
        *              The {@link IonValue} representing the results of a DML operation.
        * @return a string of document ID.
        */
       public static String getDocumentIdFromDmlResultDocument(final IonValue dmlResultDocument) {
           try {
               DmlResultDocument result = Constants.MAPPER.readValue(dmlResultDocument, DmlResultDocument.class);
               return result.getDocumentId();
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Get the String value of a given {@link IonStruct} field name.
        * @param struct the {@link IonStruct} from which to get the value.
        * @param fieldName the name of the field from which to get the value.
        * @return the String value of the field within the given {@link IonStruct}.
        */
       public static String getStringValueOfStructField(final IonStruct struct, final String fieldName) {
           return ((IonString) struct.get(fieldName)).stringValue();
       }
   
       /**
        * Return a copy of the given driver's license with updated person Id.
        *
        * @param oldLicense
        *              The old driver's license to update.
        * @param personId
        *              The PersonId of the driver.
        * @return the updated {@link DriversLicense}.
        */
       public static DriversLicense updatePersonIdDriversLicense(final DriversLicense oldLicense, final String personId) {
           return new DriversLicense(personId, oldLicense.getLicenseNumber(), oldLicense.getLicenseType(),
                   oldLicense.getValidFromDate(), oldLicense.getValidToDate());
       }
   
       /**
        * Return a copy of the given vehicle registration with updated person Id.
        *
        * @param oldRegistration
        *              The old vehicle registration to update.
        * @param personId
        *              The PersonId of the driver.
        * @return the updated {@link VehicleRegistration}.
        */
       public static VehicleRegistration updateOwnerVehicleRegistration(final VehicleRegistration oldRegistration,
                                                                        final String personId) {
           return new VehicleRegistration(oldRegistration.getVin(), oldRegistration.getLicensePlateNumber(),
                   oldRegistration.getState(), oldRegistration.getCity(), oldRegistration.getPendingPenaltyTicketAmount(),
                   oldRegistration.getValidFromDate(), oldRegistration.getValidToDate(),
                   new Owners(new Owner(personId), Collections.emptyList()));
       }
   }
   ```

------
#### [ 1.x ]

**Importante**  
Per il pacchetto Amazon Ion, devi utilizzare lo spazio dei nomi `com.amazon.ion` nella tua applicazione. AWS SDK per Java Dipende da un altro pacchetto Ion nello spazio dei nomi`software.amazon.ion`, ma si tratta di un pacchetto legacy non compatibile con il driver QLDB.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial.model;
   
   import com.amazon.ion.IonString;
   import com.amazon.ion.IonStruct;
   import com.amazon.ion.IonValue;
   import software.amazon.qldb.QldbSession;
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.Constants;
   import software.amazon.qldb.tutorial.qldb.DmlResultDocument;
   import software.amazon.qldb.tutorial.qldb.QldbRevision;
   
   import java.io.IOException;
   
   import java.math.BigDecimal;
   import java.text.ParseException;
   import java.time.LocalDate;
   import java.time.format.DateTimeFormatter;
   import java.util.ArrayList;
   import java.util.Arrays;
   import java.util.Collections;
   import java.util.List;
   
   /**
    * Sample domain objects for use throughout this tutorial.
    */
   public final class SampleData {
       public static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
   
       public static final List<VehicleRegistration> REGISTRATIONS = Collections.unmodifiableList(Arrays.asList(
               new VehicleRegistration("1N4AL11D75C109151", "LEWISR261LL", "WA", "Seattle",
                       BigDecimal.valueOf(90.25), convertToLocalDate("2017-08-21"), convertToLocalDate("2020-05-11"),
                       new Owners(new Owner(null), Collections.emptyList())),
               new VehicleRegistration("KM8SRDHF6EU074761", "CA762X", "WA", "Kent",
                       BigDecimal.valueOf(130.75), convertToLocalDate("2017-09-14"), convertToLocalDate("2020-06-25"),
                       new Owners(new Owner(null), Collections.emptyList())),
               new VehicleRegistration("3HGGK5G53FM761765", "CD820Z", "WA", "Everett",
                       BigDecimal.valueOf(442.30), convertToLocalDate("2011-03-17"), convertToLocalDate("2021-03-24"),
                       new Owners(new Owner(null), Collections.emptyList())),
               new VehicleRegistration("1HVBBAANXWH544237", "LS477D", "WA", "Tacoma",
                       BigDecimal.valueOf(42.20), convertToLocalDate("2011-10-26"), convertToLocalDate("2023-09-25"),
                       new Owners(new Owner(null), Collections.emptyList())),
               new VehicleRegistration("1C4RJFAG0FC625797", "TH393F", "WA", "Olympia",
                       BigDecimal.valueOf(30.45), convertToLocalDate("2013-09-02"), convertToLocalDate("2024-03-19"),
                       new Owners(new Owner(null), Collections.emptyList()))
       ));
   
       public static final List<Vehicle> VEHICLES = Collections.unmodifiableList(Arrays.asList(
               new Vehicle("1N4AL11D75C109151", "Sedan", 2011,  "Audi", "A5", "Silver"),
               new Vehicle("KM8SRDHF6EU074761", "Sedan", 2015, "Tesla", "Model S", "Blue"),
               new Vehicle("3HGGK5G53FM761765", "Motorcycle", 2011, "Ducati", "Monster 1200", "Yellow"),
               new Vehicle("1HVBBAANXWH544237", "Semi", 2009, "Ford", "F 150", "Black"),
               new Vehicle("1C4RJFAG0FC625797", "Sedan", 2019, "Mercedes", "CLK 350", "White")
       ));
   
       public static final List<Person> PEOPLE = Collections.unmodifiableList(Arrays.asList(
               new Person("Raul", "Lewis", convertToLocalDate("1963-08-19"),
                       "LEWISR261LL", "Driver License",  "1719 University Street, Seattle, WA, 98109"),
               new Person("Brent", "Logan", convertToLocalDate("1967-07-03"),
                       "LOGANB486CG", "Driver License", "43 Stockert Hollow Road, Everett, WA, 98203"),
               new Person("Alexis", "Pena", convertToLocalDate("1974-02-10"),
                       "744 849 301", "SSN", "4058 Melrose Street, Spokane Valley, WA, 99206"),
               new Person("Melvin", "Parker", convertToLocalDate("1976-05-22"),
                       "P626-168-229-765", "Passport", "4362 Ryder Avenue, Seattle, WA, 98101"),
               new Person("Salvatore", "Spencer", convertToLocalDate("1997-11-15"),
                       "S152-780-97-415-0", "Passport", "4450 Honeysuckle Lane, Seattle, WA, 98101")
       ));
   
       public static final List<DriversLicense> LICENSES = Collections.unmodifiableList(Arrays.asList(
               new DriversLicense(null, "LEWISR261LL", "Learner",
                       convertToLocalDate("2016-12-20"), convertToLocalDate("2020-11-15")),
               new DriversLicense(null, "LOGANB486CG", "Probationary",
                       convertToLocalDate("2016-04-06"), convertToLocalDate("2020-11-15")),
               new DriversLicense(null, "744 849 301", "Full",
                       convertToLocalDate("2017-12-06"), convertToLocalDate("2022-10-15")),
               new DriversLicense(null, "P626-168-229-765", "Learner",
                       convertToLocalDate("2017-08-16"), convertToLocalDate("2021-11-15")),
               new DriversLicense(null, "S152-780-97-415-0", "Probationary",
                       convertToLocalDate("2015-08-15"), convertToLocalDate("2021-08-21"))
       ));
   
       private SampleData() { }
   
       /**
        * Converts a date string with the format 'yyyy-MM-dd' into a {@link java.util.Date} object.
        *
        * @param date
        *              The date string to convert.
        * @return {@link LocalDate} or null if there is a {@link ParseException}
        */
       public static synchronized LocalDate convertToLocalDate(String date) {
           return LocalDate.parse(date, DATE_TIME_FORMAT);
       }
   
       /**
        * Convert the result set into a list of IonValues.
        *
        * @param result
        *              The result set to convert.
        * @return a list of IonValues.
        */
       public static List<IonValue> toIonValues(Result result) {
           final List<IonValue> valueList = new ArrayList<>();
           result.iterator().forEachRemaining(valueList::add);
           return valueList;
       }
   
       /**
        * Get the document ID of a particular document.
        *
        * @param txn
        *              A transaction executor object.
        * @param tableName
        *              Name of the table containing the document.
        * @param identifier
        *              The identifier used to narrow down the search.
        * @param value
        *              Value of the identifier.
        * @return the list of document IDs in the result set.
        */
       public static String getDocumentId(final TransactionExecutor txn, final String tableName,
                                          final String identifier, final String value) {
           try {
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(value));
               final String query = String.format("SELECT metadata.id FROM _ql_committed_%s AS p WHERE p.data.%s = ?",
                       tableName, identifier);
               Result result = txn.execute(query, parameters);
               if (result.isEmpty()) {
                   throw new IllegalStateException("Unable to retrieve document ID using " + value);
               }
               return getStringValueOfStructField((IonStruct) result.iterator().next(), "id");
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Get the document by ID.
        *
        * @param qldbSession
        *              A QLDB session.
        * @param tableName
        *              Name of the table to insert documents into.
        * @param documentId
        *              The unique ID of a document in the Person table.
        * @return a {@link QldbRevision} object.
        * @throws IllegalStateException if failed to convert parameter into {@link IonValue}.
        */
       public static QldbRevision getDocumentById(QldbSession qldbSession, String tableName, String documentId) {
           try {
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(documentId));
               final String query = String.format("SELECT c.* FROM _ql_committed_%s AS c BY docId WHERE docId = ?", tableName);
               Result result = qldbSession.execute(query, parameters);
               if (result.isEmpty()) {
                   throw new IllegalStateException("Unable to retrieve document by id " + documentId + " in table " + tableName);
               }
               return Constants.MAPPER.readValue(result.iterator().next(), QldbRevision.class);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Return a list of modified document IDs as strings from a DML {@link Result}.
        *
        * @param result
        *              The result set from a DML operation.
        * @return the list of document IDs modified by the operation.
        */
       public static List<String> getDocumentIdsFromDmlResult(final Result result) {
           final List<String> strings = new ArrayList<>();
           result.iterator().forEachRemaining(row -> strings.add(getDocumentIdFromDmlResultDocument(row)));
           return strings;
       }
   
       /**
        * Convert the given DML result row's document ID to string.
        *
        * @param dmlResultDocument
        *              The {@link IonValue} representing the results of a DML operation.
        * @return a string of document ID.
        */
       public static String getDocumentIdFromDmlResultDocument(final IonValue dmlResultDocument) {
           try {
               DmlResultDocument result = Constants.MAPPER.readValue(dmlResultDocument, DmlResultDocument.class);
               return result.getDocumentId();
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Get the String value of a given {@link IonStruct} field name.
        * @param struct the {@link IonStruct} from which to get the value.
        * @param fieldName the name of the field from which to get the value.
        * @return the String value of the field within the given {@link IonStruct}.
        */
       public static String getStringValueOfStructField(final IonStruct struct, final String fieldName) {
           return ((IonString) struct.get(fieldName)).stringValue();
       }
   
       /**
        * Return a copy of the given driver's license with updated person Id.
        *
        * @param oldLicense
        *              The old driver's license to update.
        * @param personId
        *              The PersonId of the driver.
        * @return the updated {@link DriversLicense}.
        */
       public static DriversLicense updatePersonIdDriversLicense(final DriversLicense oldLicense, final String personId) {
           return new DriversLicense(personId, oldLicense.getLicenseNumber(), oldLicense.getLicenseType(),
                   oldLicense.getValidFromDate(), oldLicense.getValidToDate());
       }
   
       /**
        * Return a copy of the given vehicle registration with updated person Id.
        *
        * @param oldRegistration
        *              The old vehicle registration to update.
        * @param personId
        *              The PersonId of the driver.
        * @return the updated {@link VehicleRegistration}.
        */
       public static VehicleRegistration updateOwnerVehicleRegistration(final VehicleRegistration oldRegistration,
                                                                        final String personId) {
           return new VehicleRegistration(oldRegistration.getVin(), oldRegistration.getLicensePlateNumber(),
                   oldRegistration.getState(), oldRegistration.getCity(), oldRegistration.getPendingPenaltyTicketAmount(),
                   oldRegistration.getValidFromDate(), oldRegistration.getValidToDate(),
                   new Owners(new Owner(personId), Collections.emptyList()));
       }
   }
   ```

------
**Nota**  
Questa classe utilizza le librerie Ion per fornire metodi di supporto che convertono i dati da e verso il formato Ion.
Il `getDocumentId` metodo esegue una query su una tabella con il prefisso`_ql_committed_`. Si tratta di un prefisso riservato che indica che si desidera interrogare la *visualizzazione confermata di una tabella*. In questa visualizzazione, i dati sono annidati nel `data` campo e i metadati sono annidati nel campo. `metadata`

1. Compila ed esegui il seguente programma (`CreateTable.java`) per creare le tabelle menzionate in precedenza.

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.SampleData;
   
   /**
    * Create tables in a QLDB ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class CreateTable {
       public static final Logger log = LoggerFactory.getLogger(CreateTable.class);
   
       private CreateTable() { }
   
       /**
        * Registrations, vehicles, owners, and licenses tables being created in a single transaction.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param tableName
        *              Name of the table to be created.
        * @return the number of tables created.
        */
       public static int createTable(final TransactionExecutor txn, final String tableName) {
           log.info("Creating the '{}' table...", tableName);
           final String createTable = String.format("CREATE TABLE %s", tableName);
           final Result result = txn.execute(createTable);
           log.info("{} table created successfully.", tableName);
           return SampleData.toIonValues(result).size();
       }
   
       public static void main(final String... args) {
           ConnectToLedger.getDriver().execute(txn -> {
               createTable(txn, Constants.DRIVERS_LICENSE_TABLE_NAME);
               createTable(txn, Constants.PERSON_TABLE_NAME);
               createTable(txn, Constants.VEHICLE_TABLE_NAME);
               createTable(txn, Constants.VEHICLE_REGISTRATION_TABLE_NAME);
           });
       }
   }
   ```

------
#### [ 1.x ]

   ```
   /*
    * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.SampleData;
   
   /**
    * Create tables in a QLDB ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class CreateTable {
       public static final Logger log = LoggerFactory.getLogger(CreateTable.class);
   
       private CreateTable() { }
   
       /**
        * Registrations, vehicles, owners, and licenses tables being created in a single transaction.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param tableName
        *              Name of the table to be created.
        * @return the number of tables created.
        */
       public static int createTable(final TransactionExecutor txn, final String tableName) {
           log.info("Creating the '{}' table...", tableName);
           final String createTable = String.format("CREATE TABLE %s", tableName);
           final Result result = txn.execute(createTable);
           log.info("{} table created successfully.", tableName);
           return SampleData.toIonValues(result).size();
       }
   
       public static void main(final String... args) {
           ConnectToLedger.getDriver().execute(txn -> {
               createTable(txn, Constants.DRIVERS_LICENSE_TABLE_NAME);
               createTable(txn, Constants.PERSON_TABLE_NAME);
               createTable(txn, Constants.VEHICLE_TABLE_NAME);
               createTable(txn, Constants.VEHICLE_REGISTRATION_TABLE_NAME);
           }, (retryAttempt) -> log.info("Retrying due to OCC conflict..."));
       }
   }
   ```

------
**Nota**  
Questo programma dimostra come passare un `TransactionExecutor` lambda al metodo. `execute` In questo esempio, si eseguono più istruzioni `CREATE TABLE` PartiQL in una singola transazione utilizzando un'espressione lambda.  
Il `execute` metodo avvia implicitamente una transazione, esegue tutte le istruzioni in lambda e quindi esegue automaticamente il commit della transazione.

1. Compila ed esegui il seguente programma (`CreateIndex.java`) per creare indici sulle tabelle, come descritto in precedenza.

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.SampleData;
   
   /**
    * Create indexes on tables in a particular ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class CreateIndex {
       public static final Logger log = LoggerFactory.getLogger(CreateIndex.class);
   
       private CreateIndex() { }
   
       /**
        * In this example, create indexes for registrations and vehicles tables.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param tableName
        *              Name of the table to be created.
        * @param indexAttribute
        *              The index attribute to use.
        * @return the number of tables created.
        */
       public static int createIndex(final TransactionExecutor txn, final String tableName, final String indexAttribute) {
           log.info("Creating an index on {}...", indexAttribute);
           final String createIndex = String.format("CREATE INDEX ON %s (%s)", tableName, indexAttribute);
           final Result r = txn.execute(createIndex);
           return SampleData.toIonValues(r).size();
       }
   
       public static void main(final String... args) {
           ConnectToLedger.getDriver().execute(txn -> {
               createIndex(txn, Constants.PERSON_TABLE_NAME, Constants.PERSON_GOV_ID_INDEX_NAME);
               createIndex(txn, Constants.VEHICLE_TABLE_NAME, Constants.VIN_INDEX_NAME);
               createIndex(txn, Constants.DRIVERS_LICENSE_TABLE_NAME, Constants.DRIVER_LICENSE_NUMBER_INDEX_NAME);
               createIndex(txn, Constants.DRIVERS_LICENSE_TABLE_NAME, Constants.DRIVER_LICENSE_PERSONID_INDEX_NAME);
               createIndex(txn, Constants.VEHICLE_REGISTRATION_TABLE_NAME, Constants.VIN_INDEX_NAME);
               createIndex(txn, Constants.VEHICLE_REGISTRATION_TABLE_NAME,
                       Constants.VEHICLE_REGISTRATION_LICENSE_PLATE_NUMBER_INDEX_NAME);
           });
           log.info("Indexes created successfully!");
       }
   }
   ```

------
#### [ 1.x ]

   ```
   /*
    * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.SampleData;
   
   /**
    * Create indexes on tables in a particular ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class CreateIndex {
       public static final Logger log = LoggerFactory.getLogger(CreateIndex.class);
   
       private CreateIndex() { }
   
       /**
        * In this example, create indexes for registrations and vehicles tables.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param tableName
        *              Name of the table to be created.
        * @param indexAttribute
        *              The index attribute to use.
        * @return the number of tables created.
        */
       public static int createIndex(final TransactionExecutor txn, final String tableName, final String indexAttribute) {
           log.info("Creating an index on {}...", indexAttribute);
           final String createIndex = String.format("CREATE INDEX ON %s (%s)", tableName, indexAttribute);
           final Result r = txn.execute(createIndex);
           return SampleData.toIonValues(r).size();
       }
   
       public static void main(final String... args) {
           ConnectToLedger.getDriver().execute(txn -> {
               createIndex(txn, Constants.PERSON_TABLE_NAME, Constants.PERSON_GOV_ID_INDEX_NAME);
               createIndex(txn, Constants.VEHICLE_TABLE_NAME, Constants.VIN_INDEX_NAME);
               createIndex(txn, Constants.DRIVERS_LICENSE_TABLE_NAME, Constants.DRIVER_LICENSE_NUMBER_INDEX_NAME);
               createIndex(txn, Constants.DRIVERS_LICENSE_TABLE_NAME, Constants.DRIVER_LICENSE_PERSONID_INDEX_NAME);
               createIndex(txn, Constants.VEHICLE_REGISTRATION_TABLE_NAME, Constants.VIN_INDEX_NAME);
               createIndex(txn, Constants.VEHICLE_REGISTRATION_TABLE_NAME,
                       Constants.VEHICLE_REGISTRATION_LICENSE_PLATE_NUMBER_INDEX_NAME);
           }, (retryAttempt) -> log.info("Retrying due to OCC conflict..."));
           log.info("Indexes created successfully!");
       }
   }
   ```

------

1. Compilate ed eseguite il seguente programma (`InsertDocument.java`) per inserire i dati di esempio nelle tabelle.

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import java.io.IOException;
   import java.util.ArrayList;
   import java.util.Collections;
   import java.util.List;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import com.amazon.ion.IonValue;
   
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.DriversLicense;
   import software.amazon.qldb.tutorial.model.SampleData;
   import software.amazon.qldb.tutorial.model.VehicleRegistration;
   
   /**
    * Insert documents into a table in a QLDB ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class InsertDocument {
       public static final Logger log = LoggerFactory.getLogger(InsertDocument.class);
   
       private InsertDocument() { }
   
       /**
        * Insert the given list of documents into the specified table and return the document IDs of the inserted documents.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param tableName
        *              Name of the table to insert documents into.
        * @param documents
        *              List of documents to insert into the specified table.
        * @return a list of document IDs.
        * @throws IllegalStateException if failed to convert documents into an {@link IonValue}.
        */
       public static List<String> insertDocuments(final TransactionExecutor txn, final String tableName,
                                                  final List documents) {
           log.info("Inserting some documents in the {} table...", tableName);
           try {
               final String query = String.format("INSERT INTO %s ?", tableName);
               final IonValue ionDocuments = Constants.MAPPER.writeValueAsIonValue(documents);
   
               return SampleData.getDocumentIdsFromDmlResult(txn.execute(query, ionDocuments));
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Update PersonIds in driver's licenses and in vehicle registrations using document IDs.
        *
        * @param documentIds
        *              List of document IDs representing the PersonIds in DriversLicense and PrimaryOwners in VehicleRegistration.
        * @param licenses
        *              List of driver's licenses to update.
        * @param registrations
        *              List of registrations to update.
        */
       public static void updatePersonId(final List<String> documentIds, final List<DriversLicense> licenses,
                                         final List<VehicleRegistration> registrations) {
           for (int i = 0; i < documentIds.size(); ++i) {
               DriversLicense license = SampleData.LICENSES.get(i);
               VehicleRegistration registration = SampleData.REGISTRATIONS.get(i);
               licenses.add(SampleData.updatePersonIdDriversLicense(license, documentIds.get(i)));
               registrations.add(SampleData.updateOwnerVehicleRegistration(registration, documentIds.get(i)));
           }
       }
   
       public static void main(final String... args) {
           final List<DriversLicense> newDriversLicenses = new ArrayList<>();
           final List<VehicleRegistration> newVehicleRegistrations = new ArrayList<>();
           ConnectToLedger.getDriver().execute(txn -> {
               List<String> documentIds = insertDocuments(txn, Constants.PERSON_TABLE_NAME, SampleData.PEOPLE);
               updatePersonId(documentIds, newDriversLicenses, newVehicleRegistrations);
               insertDocuments(txn, Constants.VEHICLE_TABLE_NAME, SampleData.VEHICLES);
               insertDocuments(txn, Constants.VEHICLE_REGISTRATION_TABLE_NAME,
                       Collections.unmodifiableList(newVehicleRegistrations));
               insertDocuments(txn, Constants.DRIVERS_LICENSE_TABLE_NAME,
                       Collections.unmodifiableList(newDriversLicenses));
           });
           log.info("Documents inserted successfully!");
       }
   }
   ```

------
#### [ 1.x ]

   ```
   /*
    * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazon.ion.IonValue;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   import software.amazon.qldb.QldbSession;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.DriversLicense;
   import software.amazon.qldb.tutorial.model.SampleData;
   import software.amazon.qldb.tutorial.model.VehicleRegistration;
   
   import java.io.IOException;
   import java.util.ArrayList;
   import java.util.Collections;
   import java.util.List;
   
   /**
    * Insert documents into a table in a QLDB ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class InsertDocument {
       public static final Logger log = LoggerFactory.getLogger(InsertDocument.class);
   
       private InsertDocument() { }
   
       /**
        * Insert the given list of documents into the specified table and return the document IDs of the inserted documents.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param tableName
        *              Name of the table to insert documents into.
        * @param documents
        *              List of documents to insert into the specified table.
        * @return a list of document IDs.
        * @throws IllegalStateException if failed to convert documents into an {@link IonValue}.
        */
       public static List<String> insertDocuments(final TransactionExecutor txn, final String tableName,
                                                  final List documents) {
           log.info("Inserting some documents in the {} table...", tableName);
           try {
               final String statement = String.format("INSERT INTO %s ?", tableName);
               final IonValue ionDocuments = Constants.MAPPER.writeValueAsIonValue(documents);
               final List<IonValue> parameters = Collections.singletonList(ionDocuments);
               return SampleData.getDocumentIdsFromDmlResult(txn.execute(statement, parameters));
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Update PersonIds in driver's licenses and in vehicle registrations using document IDs.
        *
        * @param documentIds
        *              List of document IDs representing the PersonIds in DriversLicense and PrimaryOwners in VehicleRegistration.
        * @param licenses
        *              List of driver's licenses to update.
        * @param registrations
        *              List of registrations to update.
        */
       public static void updatePersonId(final List<String> documentIds, final List<DriversLicense> licenses,
                                         final List<VehicleRegistration> registrations) {
           for (int i = 0; i < documentIds.size(); ++i) {
               DriversLicense license = SampleData.LICENSES.get(i);
               VehicleRegistration registration = SampleData.REGISTRATIONS.get(i);
               licenses.add(SampleData.updatePersonIdDriversLicense(license, documentIds.get(i)));
               registrations.add(SampleData.updateOwnerVehicleRegistration(registration, documentIds.get(i)));
           }
       }
   
       public static void main(final String... args) {
           final List<DriversLicense> newDriversLicenses = new ArrayList<>();
           final List<VehicleRegistration> newVehicleRegistrations = new ArrayList<>();
           ConnectToLedger.getDriver().execute(txn -> {
               List<String> documentIds = insertDocuments(txn, Constants.PERSON_TABLE_NAME, SampleData.PEOPLE);
               updatePersonId(documentIds, newDriversLicenses, newVehicleRegistrations);
               insertDocuments(txn, Constants.VEHICLE_TABLE_NAME, SampleData.VEHICLES);
               insertDocuments(txn, Constants.VEHICLE_REGISTRATION_TABLE_NAME,
                       Collections.unmodifiableList(newVehicleRegistrations));
               insertDocuments(txn, Constants.DRIVERS_LICENSE_TABLE_NAME,
                       Collections.unmodifiableList(newDriversLicenses));
           }, (retryAttempt) -> log.info("Retrying due to OCC conflict..."));
           log.info("Documents inserted successfully!");
       }
   }
   ```

------
**Nota**  
Questo programma dimostra come chiamare il `execute` metodo con valori parametrizzati. È possibile passare parametri di dati di tipo oltre `IonValue` all'istruzione PartiQL che si desidera eseguire. Utilizzate un punto interrogativo (`?`) come segnaposto variabile nella stringa dell'istruzione.
Se un'`INSERT`istruzione ha esito positivo, restituisce il valore `id` di ogni documento inserito.

Successivamente, è possibile utilizzare `SELECT` le istruzioni per leggere i dati dalle tabelle del `vehicle-registration` registro. Passa a [Passaggio 4: interrogare le tabelle in un libro mastro](getting-started.java.step-4.md).

# Passaggio 4: interrogare le tabelle in un libro mastro
Fase 4: Interrogare le tabelle

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Dopo aver creato tabelle in un registro Amazon QLDB e averle caricate con i dati, puoi eseguire query per rivedere i dati di immatricolazione dei veicoli che hai appena inserito. QLDB [utilizza](ql-reference.md) PartiQL come linguaggio di interrogazione [e Amazon](ion.md) Ion come modello di dati orientato ai documenti.

PartiQL è un linguaggio di query open source compatibile con SQL che è stato esteso per funzionare con Ion. Con PartiQL, puoi inserire, interrogare e gestire i tuoi dati con operatori SQL familiari. Amazon Ion è un superset di JSON. Ion è un formato di dati open source basato su documenti che offre la flessibilità di archiviazione ed elaborazione di dati strutturati, semistrutturati e annidati.

In questo passaggio, si utilizzano `SELECT` le istruzioni per leggere i dati dalle tabelle del registro. `vehicle-registration`

**avvertimento**  
Quando si esegue una query in QLDB senza una ricerca indicizzata, viene richiamata una scansione completa della tabella. PartiQL supporta tali query perché è compatibile con SQL. Tuttavia, *non* eseguire scansioni di tabelle per casi d'uso di produzione in QLDB. Le scansioni delle tabelle possono causare problemi di prestazioni su tabelle di grandi dimensioni, inclusi conflitti di concorrenza e timeout delle transazioni.  
Per evitare le scansioni delle tabelle, è necessario 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)` Per ulteriori informazioni, consulta [Ottimizzazione delle prestazioni delle query](working.optimize.md).

**Per interrogare le tabelle**
+ Compila ed esegui il seguente programma (`FindVehicles.java`) per interrogare tutti i veicoli registrati con una persona nel tuo registro.

------
#### [ 2.x ]

  ```
  /*
   * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   * SPDX-License-Identifier: MIT-0
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   * software and associated documentation files (the "Software"), to deal in the Software
   * without restriction, including without limitation the rights to use, copy, modify,
   * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  package software.amazon.qldb.tutorial;
  
  import java.io.IOException;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.amazon.ion.IonValue;
  
  import software.amazon.qldb.Result;
  import software.amazon.qldb.TransactionExecutor;
  import software.amazon.qldb.tutorial.model.Person;
  import software.amazon.qldb.tutorial.model.SampleData;
  
  /**
   * Find all vehicles registered under a person.
   *
   * This code expects that you have AWS credentials setup per:
   * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
   */
  public final class FindVehicles {
      public static final Logger log = LoggerFactory.getLogger(FindVehicles.class);
  
      private FindVehicles() { }
  
      /**
       * Find vehicles registered under a driver using their government ID.
       *
       * @param txn
       *              The {@link TransactionExecutor} for lambda execute.
       * @param govId
       *              The government ID of the owner.
       * @throws IllegalStateException if failed to convert parameters into {@link IonValue}.
       */
      public static void findVehiclesForOwner(final TransactionExecutor txn, final String govId) {
          try {
              final String documentId = Person.getDocumentIdByGovId(txn, govId);
              final String query = "SELECT v FROM Vehicle AS v INNER JOIN VehicleRegistration AS r "
                      + "ON v.VIN = r.VIN WHERE r.Owners.PrimaryOwner.PersonId = ?";
  
              final Result result = txn.execute(query, Constants.MAPPER.writeValueAsIonValue(documentId));
              log.info("List of Vehicles for owner with GovId: {}...", govId);
              ScanTable.printDocuments(result);
          } catch (IOException ioe) {
              throw new IllegalStateException(ioe);
          }
      }
  
      public static void main(final String... args) {
          final Person person = SampleData.PEOPLE.get(0);
          ConnectToLedger.getDriver().execute(txn -> {
              findVehiclesForOwner(txn, person.getGovId());
          });
      }
  }
  ```

------
#### [ 1.x ]

  ```
  /*
   * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
   * SPDX-License-Identifier: MIT-0
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   * software and associated documentation files (the "Software"), to deal in the Software
   * without restriction, including without limitation the rights to use, copy, modify,
   * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  package software.amazon.qldb.tutorial;
  
  import java.io.IOException;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.amazon.ion.IonValue;
  
  import software.amazon.qldb.Result;
  import software.amazon.qldb.TransactionExecutor;
  import software.amazon.qldb.tutorial.model.Person;
  import software.amazon.qldb.tutorial.model.SampleData;
  
  /**
   * Find all vehicles registered under a person.
   *
   * This code expects that you have AWS credentials setup per:
   * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
   */
  public final class FindVehicles {
      public static final Logger log = LoggerFactory.getLogger(FindVehicles.class);
  
      private FindVehicles() { }
  
      /**
       * Find vehicles registered under a driver using their government ID.
       *
       * @param txn
       *              The {@link TransactionExecutor} for lambda execute.
       * @param govId
       *              The government ID of the owner.
       * @throws IllegalStateException if failed to convert parameters into {@link IonValue}.
       */
      public static void findVehiclesForOwner(final TransactionExecutor txn, final String govId) {
          try {
              final String documentId = Person.getDocumentIdByGovId(txn, govId);
              final String query = "SELECT v FROM Vehicle AS v INNER JOIN VehicleRegistration AS r "
                      + "ON v.VIN = r.VIN WHERE r.Owners.PrimaryOwner.PersonId = ?";
  
              final Result result = txn.execute(query, Constants.MAPPER.writeValueAsIonValue(documentId));
              log.info("List of Vehicles for owner with GovId: {}...", govId);
              ScanTable.printDocuments(result);
          } catch (IOException ioe) {
              throw new IllegalStateException(ioe);
          }
      }
  
      public static void main(final String... args) {
          final Person person = SampleData.PEOPLE.get(0);
          ConnectToLedger.getDriver().execute(txn -> {
              findVehiclesForOwner(txn, person.getGovId());
          }, (retryAttempt) -> log.info("Retrying due to OCC conflict..."));
      }
  }
  ```

------
**Nota**  
Innanzitutto, questo programma interroga la `Person` tabella del documento per ottenere il relativo campo `GovId LEWISR261LL` di metadati. `id`  
Quindi, utilizza questo documento `id` come chiave esterna per interrogare la `VehicleRegistration` tabella. `PrimaryOwner.PersonId` Inoltre si unisce `VehicleRegistration` alla `Vehicle` tabella sul `VIN` campo.

Per ulteriori informazioni sulla modifica dei documenti nelle tabelle del `vehicle-registration` libro mastro, consulta. [Fase 5: Modificare i documenti in un libro mastro](getting-started.java.step-5.md)

# Fase 5: Modificare i documenti in un libro mastro
Fase 5: Modificare i documenti

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Ora che hai dei dati su cui lavorare, puoi iniziare a modificare i documenti nel `vehicle-registration` registro di Amazon QLDB. In questa fase, i seguenti esempi di codice mostrano come eseguire istruzioni DML (Data Manipulation Language). Queste istruzioni aggiornano il proprietario principale di un veicolo e aggiungono un proprietario secondario a un altro veicolo.

**Per modificare i documenti**

1. Compila ed esegui il seguente programma (`TransferVehicleOwnership.java`) per aggiornare il proprietario principale del veicolo con il VIN `1N4AL11D75C109151` nel registro.

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazon.ion.IonReader;
   import com.amazon.ion.IonStruct;
   import com.amazon.ion.IonValue;
   import com.amazon.ion.system.IonReaderBuilder;
   
   import java.io.IOException;
   import java.util.ArrayList;
   import java.util.Collections;
   import java.util.LinkedHashMap;
   import java.util.List;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.Owner;
   import software.amazon.qldb.tutorial.model.Person;
   import software.amazon.qldb.tutorial.model.SampleData;
   
   /**
    * Find primary owner for a particular vehicle's VIN.
    * Transfer to another primary owner for a particular vehicle's VIN.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class TransferVehicleOwnership {
       public static final Logger log = LoggerFactory.getLogger(TransferVehicleOwnership.class);
   
       private TransferVehicleOwnership() { }
   
       /**
        * Query a driver's information using the given ID.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param documentId
        *              The unique ID of a document in the Person table.
        * @return a {@link Person} object.
        * @throws IllegalStateException if failed to convert parameter into {@link IonValue}.
        */
       public static Person findPersonFromDocumentId(final TransactionExecutor txn, final String documentId) {
           try {
               log.info("Finding person for documentId: {}...", documentId);
               final String query = "SELECT p.* FROM Person AS p BY pid WHERE pid = ?";
   
               Result result = txn.execute(query, Constants.MAPPER.writeValueAsIonValue(documentId));
               if (result.isEmpty()) {
                   throw new IllegalStateException("Unable to find person with ID: " + documentId);
               }
   
               return Constants.MAPPER.readValue(result.iterator().next(), Person.class);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Find the primary owner for the given VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              Unique VIN for a vehicle.
        * @return a {@link Person} object.
        * @throws IllegalStateException if failed to convert parameter into {@link IonValue}.
        */
       public static Person findPrimaryOwnerForVehicle(final TransactionExecutor txn, final String vin) {
           try {
               log.info("Finding primary owner for vehicle with Vin: {}...", vin);
               final String query = "SELECT Owners.PrimaryOwner.PersonId FROM VehicleRegistration AS v WHERE v.VIN = ?";
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(vin));
               Result result = txn.execute(query, parameters);
               final List<IonStruct> documents = ScanTable.toIonStructs(result);
               ScanTable.printDocuments(documents);
               if (documents.isEmpty()) {
                   throw new IllegalStateException("Unable to find registrations with VIN: " + vin);
               }
   
               final IonReader reader = IonReaderBuilder.standard().build(documents.get(0));
               final String personId = Constants.MAPPER.readValue(reader, LinkedHashMap.class).get("PersonId").toString();
               return findPersonFromDocumentId(txn, personId);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Update the primary owner for a vehicle registration with the given documentId.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              Unique VIN for a vehicle.
        * @param documentId
        *              New PersonId for the primary owner.
        * @throws IllegalStateException if no vehicle registration was found using the given document ID and VIN, or if failed
        * to convert parameters into {@link IonValue}.
        */
       public static void updateVehicleRegistration(final TransactionExecutor txn, final String vin, final String documentId) {
           try {
               log.info("Updating primary owner for vehicle with Vin: {}...", vin);
               final String query = "UPDATE VehicleRegistration AS v SET v.Owners.PrimaryOwner = ? WHERE v.VIN = ?";
   
               final List<IonValue> parameters = new ArrayList<>();
               parameters.add(Constants.MAPPER.writeValueAsIonValue(new Owner(documentId)));
               parameters.add(Constants.MAPPER.writeValueAsIonValue(vin));
   
               Result result = txn.execute(query, parameters);
               ScanTable.printDocuments(result);
               if (result.isEmpty()) {
                   throw new IllegalStateException("Unable to transfer vehicle, could not find registration.");
               } else {
                   log.info("Successfully transferred vehicle with VIN '{}' to new owner.", vin);
               }
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       public static void main(final String... args) {
           final String vin = SampleData.VEHICLES.get(0).getVin();
           final String primaryOwnerGovId = SampleData.PEOPLE.get(0).getGovId();
           final String newPrimaryOwnerGovId = SampleData.PEOPLE.get(1).getGovId();
   
           ConnectToLedger.getDriver().execute(txn -> {
               final Person primaryOwner = findPrimaryOwnerForVehicle(txn, vin);
               if (!primaryOwner.getGovId().equals(primaryOwnerGovId)) {
                   // Verify the primary owner.
                   throw new IllegalStateException("Incorrect primary owner identified for vehicle, unable to transfer.");
               }
   
               final String newOwner = Person.getDocumentIdByGovId(txn, newPrimaryOwnerGovId);
               updateVehicleRegistration(txn, vin, newOwner);
           });
           log.info("Successfully transferred vehicle ownership!");
       }
   }
   ```

------
#### [ 1.x ]

   ```
   /*
    * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazon.ion.IonReader;
   import com.amazon.ion.IonStruct;
   import com.amazon.ion.IonValue;
   import com.amazon.ion.system.IonReaderBuilder;
   
   import java.io.IOException;
   import java.util.ArrayList;
   import java.util.Collections;
   import java.util.LinkedHashMap;
   import java.util.List;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.Owner;
   import software.amazon.qldb.tutorial.model.Person;
   import software.amazon.qldb.tutorial.model.SampleData;
   
   /**
    * Find primary owner for a particular vehicle's VIN.
    * Transfer to another primary owner for a particular vehicle's VIN.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class TransferVehicleOwnership {
       public static final Logger log = LoggerFactory.getLogger(TransferVehicleOwnership.class);
   
       private TransferVehicleOwnership() { }
   
       /**
        * Query a driver's information using the given ID.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param documentId
        *              The unique ID of a document in the Person table.
        * @return a {@link Person} object.
        * @throws IllegalStateException if failed to convert parameter into {@link IonValue}.
        */
       public static Person findPersonFromDocumentId(final TransactionExecutor txn, final String documentId) {
           try {
               log.info("Finding person for documentId: {}...", documentId);
               final String query = "SELECT p.* FROM Person AS p BY pid WHERE pid = ?";
   
               Result result = txn.execute(query, Constants.MAPPER.writeValueAsIonValue(documentId));
               if (result.isEmpty()) {
                   throw new IllegalStateException("Unable to find person with ID: " + documentId);
               }
   
               return Constants.MAPPER.readValue(result.iterator().next(), Person.class);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Find the primary owner for the given VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              Unique VIN for a vehicle.
        * @return a {@link Person} object.
        * @throws IllegalStateException if failed to convert parameter into {@link IonValue}.
        */
       public static Person findPrimaryOwnerForVehicle(final TransactionExecutor txn, final String vin) {
           try {
               log.info("Finding primary owner for vehicle with Vin: {}...", vin);
               final String query = "SELECT Owners.PrimaryOwner.PersonId FROM VehicleRegistration AS v WHERE v.VIN = ?";
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(vin));
               Result result = txn.execute(query, parameters);
               final List<IonStruct> documents = ScanTable.toIonStructs(result);
               ScanTable.printDocuments(documents);
               if (documents.isEmpty()) {
                   throw new IllegalStateException("Unable to find registrations with VIN: " + vin);
               }
   
               final IonReader reader = IonReaderBuilder.standard().build(documents.get(0));
               final String personId = Constants.MAPPER.readValue(reader, LinkedHashMap.class).get("PersonId").toString();
               return findPersonFromDocumentId(txn, personId);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Update the primary owner for a vehicle registration with the given documentId.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              Unique VIN for a vehicle.
        * @param documentId
        *              New PersonId for the primary owner.
        * @throws IllegalStateException if no vehicle registration was found using the given document ID and VIN, or if failed
        * to convert parameters into {@link IonValue}.
        */
       public static void updateVehicleRegistration(final TransactionExecutor txn, final String vin, final String documentId) {
           try {
               log.info("Updating primary owner for vehicle with Vin: {}...", vin);
               final String query = "UPDATE VehicleRegistration AS v SET v.Owners.PrimaryOwner = ? WHERE v.VIN = ?";
   
               final List<IonValue> parameters = new ArrayList<>();
               parameters.add(Constants.MAPPER.writeValueAsIonValue(new Owner(documentId)));
               parameters.add(Constants.MAPPER.writeValueAsIonValue(vin));
   
               Result result = txn.execute(query, parameters);
               ScanTable.printDocuments(result);
               if (result.isEmpty()) {
                   throw new IllegalStateException("Unable to transfer vehicle, could not find registration.");
               } else {
                   log.info("Successfully transferred vehicle with VIN '{}' to new owner.", vin);
               }
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       public static void main(final String... args) {
           final String vin = SampleData.VEHICLES.get(0).getVin();
           final String primaryOwnerGovId = SampleData.PEOPLE.get(0).getGovId();
           final String newPrimaryOwnerGovId = SampleData.PEOPLE.get(1).getGovId();
   
           ConnectToLedger.getDriver().execute(txn -> {
               final Person primaryOwner = findPrimaryOwnerForVehicle(txn, vin);
               if (!primaryOwner.getGovId().equals(primaryOwnerGovId)) {
                   // Verify the primary owner.
                   throw new IllegalStateException("Incorrect primary owner identified for vehicle, unable to transfer.");
               }
   
               final String newOwner = Person.getDocumentIdByGovId(txn, newPrimaryOwnerGovId);
               updateVehicleRegistration(txn, vin, newOwner);
           }, (retryAttempt) -> log.info("Retrying due to OCC conflict..."));
           log.info("Successfully transferred vehicle ownership!");
       }
   }
   ```

------

1. Compila ed esegui il seguente programma (`AddSecondaryOwner.java`) per aggiungere un proprietario secondario al veicolo con VIN `KM8SRDHF6EU074761` nel registro.

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import java.io.IOException;
   import java.util.Collections;
   import java.util.Iterator;
   import java.util.List;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import com.amazon.ion.IonValue;
   
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.Owner;
   import software.amazon.qldb.tutorial.model.Owners;
   import software.amazon.qldb.tutorial.model.Person;
   import software.amazon.qldb.tutorial.model.SampleData;
   
   /**
    * Finds and adds secondary owners for a vehicle.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class AddSecondaryOwner {
       public static final Logger log = LoggerFactory.getLogger(AddSecondaryOwner.class);
   
       private AddSecondaryOwner() { }
   
       /**
        * Check whether a secondary owner has already been registered for the given VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              Unique VIN for a vehicle.
        * @param secondaryOwnerId
        *              The secondary owner to add.
        * @return {@code true} if the given secondary owner has already been registered, {@code false} otherwise.
        * @throws IllegalStateException if failed to convert VIN to an {@link IonValue}.
        */
       public static boolean isSecondaryOwnerForVehicle(final TransactionExecutor txn, final String vin,
                                                        final String secondaryOwnerId) {
           try {
               log.info("Finding secondary owners for vehicle with VIN: {}...", vin);
               final String query = "SELECT Owners.SecondaryOwners FROM VehicleRegistration AS v WHERE v.VIN = ?";
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(vin));
               final Result result = txn.execute(query, parameters);
               final Iterator<IonValue> itr = result.iterator();
               if (!itr.hasNext()) {
                   return false;
               }
   
               final Owners owners = Constants.MAPPER.readValue(itr.next(), Owners.class);
               if (null != owners.getSecondaryOwners()) {
                   for (Owner owner : owners.getSecondaryOwners()) {
                       if (secondaryOwnerId.equalsIgnoreCase(owner.getPersonId())) {
                           return true;
                       }
                   }
               }
   
               return false;
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Adds a secondary owner for the specified VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              Unique VIN for a vehicle.
        * @param secondaryOwner
        *              The secondary owner to add.
        * @throws IllegalStateException if failed to convert parameter into an {@link IonValue}.
        */
       public static void addSecondaryOwnerForVin(final TransactionExecutor txn, final String vin,
                                                  final String secondaryOwner) {
           try {
               log.info("Inserting secondary owner for vehicle with VIN: {}...", vin);
               final String query = String.format("FROM VehicleRegistration AS v WHERE v.VIN = ?" +
                       "INSERT INTO v.Owners.SecondaryOwners VALUE ?");
               final IonValue newOwner = Constants.MAPPER.writeValueAsIonValue(new Owner(secondaryOwner));
               final IonValue vinAsIonValue = Constants.MAPPER.writeValueAsIonValue(vin);
               Result result = txn.execute(query, vinAsIonValue, newOwner);
               log.info("VehicleRegistration Document IDs which had secondary owners added: ");
               ScanTable.printDocuments(result);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       public static void main(final String... args) {
           final String vin = SampleData.VEHICLES.get(1).getVin();
           final String govId = SampleData.PEOPLE.get(0).getGovId();
   
           ConnectToLedger.getDriver().execute(txn -> {
               final String documentId = Person.getDocumentIdByGovId(txn, govId);
               if (isSecondaryOwnerForVehicle(txn, vin, documentId)) {
                   log.info("Person with ID {} has already been added as a secondary owner of this vehicle.", govId);
               } else {
                   addSecondaryOwnerForVin(txn, vin, documentId);
               }
           });
           log.info("Secondary owners successfully updated.");
       }
   }
   ```

------
#### [ 1.x ]

   ```
   /*
    * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import java.io.IOException;
   import java.util.Collections;
   import java.util.Iterator;
   import java.util.List;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import com.amazon.ion.IonValue;
   
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.Owner;
   import software.amazon.qldb.tutorial.model.Owners;
   import software.amazon.qldb.tutorial.model.Person;
   import software.amazon.qldb.tutorial.model.SampleData;
   
   /**
    * Finds and adds secondary owners for a vehicle.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class AddSecondaryOwner {
       public static final Logger log = LoggerFactory.getLogger(AddSecondaryOwner.class);
   
       private AddSecondaryOwner() { }
   
       /**
        * Check whether a secondary owner has already been registered for the given VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              Unique VIN for a vehicle.
        * @param secondaryOwnerId
        *              The secondary owner to add.
        * @return {@code true} if the given secondary owner has already been registered, {@code false} otherwise.
        * @throws IllegalStateException if failed to convert VIN to an {@link IonValue}.
        */
       public static boolean isSecondaryOwnerForVehicle(final TransactionExecutor txn, final String vin,
                                                        final String secondaryOwnerId) {
           try {
               log.info("Finding secondary owners for vehicle with VIN: {}...", vin);
               final String query = "SELECT Owners.SecondaryOwners FROM VehicleRegistration AS v WHERE v.VIN = ?";
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(vin));
               final Result result = txn.execute(query, parameters);
               final Iterator<IonValue> itr = result.iterator();
               if (!itr.hasNext()) {
                   return false;
               }
   
               final Owners owners = Constants.MAPPER.readValue(itr.next(), Owners.class);
               if (null != owners.getSecondaryOwners()) {
                   for (Owner owner : owners.getSecondaryOwners()) {
                       if (secondaryOwnerId.equalsIgnoreCase(owner.getPersonId())) {
                           return true;
                       }
                   }
               }
   
               return false;
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Adds a secondary owner for the specified VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              Unique VIN for a vehicle.
        * @param secondaryOwner
        *              The secondary owner to add.
        * @throws IllegalStateException if failed to convert parameter into an {@link IonValue}.
        */
       public static void addSecondaryOwnerForVin(final TransactionExecutor txn, final String vin,
                                                  final String secondaryOwner) {
           try {
               log.info("Inserting secondary owner for vehicle with VIN: {}...", vin);
               final String query = String.format("FROM VehicleRegistration AS v WHERE v.VIN = '%s' " +
                       "INSERT INTO v.Owners.SecondaryOwners VALUE ?", vin);
               final IonValue newOwner = Constants.MAPPER.writeValueAsIonValue(new Owner(secondaryOwner));
               Result result = txn.execute(query, newOwner);
               log.info("VehicleRegistration Document IDs which had secondary owners added: ");
               ScanTable.printDocuments(result);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       public static void main(final String... args) {
           final String vin = SampleData.VEHICLES.get(1).getVin();
           final String govId = SampleData.PEOPLE.get(0).getGovId();
   
           ConnectToLedger.getDriver().execute(txn -> {
               final String documentId = Person.getDocumentIdByGovId(txn, govId);
               if (isSecondaryOwnerForVehicle(txn, vin, documentId)) {
                   log.info("Person with ID {} has already been added as a secondary owner of this vehicle.", govId);
               } else {
                   addSecondaryOwnerForVin(txn, vin, documentId);
               }
           }, (retryAttempt) -> log.info("Retrying due to OCC conflict..."));
           log.info("Secondary owners successfully updated.");
       }
   }
   ```

------

Per esaminare queste modifiche nel `vehicle-registration` libro mastro, consulta. [Fase 6: Visualizzare la cronologia delle revisioni di un documento](getting-started.java.step-6.md)

# Fase 6: Visualizzare la cronologia delle revisioni di un documento
Fase 6: Visualizza la cronologia delle revisioni

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Dopo aver modificato i dati di immatricolazione di un veicolo nel passaggio precedente, puoi interrogare la cronologia di tutti i proprietari registrati e qualsiasi altro campo aggiornato. In questo passaggio, esegui una query sulla cronologia delle revisioni di un documento nella `VehicleRegistration` tabella del registro. `vehicle-registration`

**Per visualizzare la cronologia delle revisioni**

1. Esamina il seguente programma (`QueryHistory.java`).

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import java.io.IOException;
   import java.time.Instant;
   import java.time.temporal.ChronoUnit;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import com.amazon.ion.IonValue;
   
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.SampleData;
   import software.amazon.qldb.tutorial.model.VehicleRegistration;
   
   /**
    * Query a table's history for a particular set of documents.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class QueryHistory {
       public static final Logger log = LoggerFactory.getLogger(QueryHistory.class);
       private static final int THREE_MONTHS = 90;
   
       private QueryHistory() { }
   
       /**
        * In this example, query the 'VehicleRegistration' history table to find all previous primary owners for a VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              VIN to find previous primary owners for.
        * @param query
        *              The query to find previous primary owners.
        * @throws IllegalStateException if failed to convert document ID to an {@link IonValue}.
        */
       public static void previousPrimaryOwners(final TransactionExecutor txn, final String vin, final String query) {
           try {
               final String docId = VehicleRegistration.getDocumentIdByVin(txn, vin);
   
               log.info("Querying the 'VehicleRegistration' table's history using VIN: {}...", vin);
               final Result result = txn.execute(query, Constants.MAPPER.writeValueAsIonValue(docId));
               ScanTable.printDocuments(result);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       public static void main(final String... args) {
           final String threeMonthsAgo = Instant.now().minus(THREE_MONTHS, ChronoUnit.DAYS).toString();
           final String query = String.format("SELECT data.Owners.PrimaryOwner, metadata.version "
                                              + "FROM history(VehicleRegistration, `%s`) "
                                              + "AS h WHERE h.metadata.id = ?", threeMonthsAgo);
           ConnectToLedger.getDriver().execute(txn -> {
               final String vin = SampleData.VEHICLES.get(0).getVin();
               previousPrimaryOwners(txn, vin, query);
           });
           log.info("Successfully queried history.");
       }
   }
   ```

------
#### [ 1.x ]

   ```
   /*
    * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazon.ion.IonValue;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   import software.amazon.qldb.QldbSession;
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.SampleData;
   import software.amazon.qldb.tutorial.model.VehicleRegistration;
   
   import java.io.IOException;
   import java.time.Instant;
   import java.time.temporal.ChronoUnit;
   import java.util.Collections;
   import java.util.List;
   
   /**
    * Query a table's history for a particular set of documents.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class QueryHistory {
       public static final Logger log = LoggerFactory.getLogger(QueryHistory.class);
       private static final int THREE_MONTHS = 90;
   
       private QueryHistory() { }
   
       /**
        * In this example, query the 'VehicleRegistration' history table to find all previous primary owners for a VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              VIN to find previous primary owners for.
        * @param query
        *              The query to find previous primary owners.
        * @throws IllegalStateException if failed to convert document ID to an {@link IonValue}.
        */
       public static void previousPrimaryOwners(final TransactionExecutor txn, final String vin, final String query) {
           try {
               final String docId = VehicleRegistration.getDocumentIdByVin(txn, vin);
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(docId));
               log.info("Querying the 'VehicleRegistration' table's history using VIN: {}...", vin);
               final Result result = txn.execute(query, parameters);
               ScanTable.printDocuments(result);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       public static void main(final String... args) {
           final String threeMonthsAgo = Instant.now().minus(THREE_MONTHS, ChronoUnit.DAYS).toString();
           final String query = String.format("SELECT data.Owners.PrimaryOwner, metadata.version "
                                              + "FROM history(VehicleRegistration, `%s`) "
                                              + "AS h WHERE h.metadata.id = ?", threeMonthsAgo);
           ConnectToLedger.getDriver().execute(txn -> {
               final String vin = SampleData.VEHICLES.get(0).getVin();
               previousPrimaryOwners(txn, vin, query);
           }, (retryAttempt) -> log.info("Retrying due to OCC conflict..."));
           log.info("Successfully queried history.");
       }
   }
   ```

------
**Nota**  
È possibile visualizzare la cronologia delle revisioni di un documento interrogando la sintassi incorporata [Funzione di cronologia](working.history.md#working.history.function) nella seguente sintassi.  

     ```
     SELECT * FROM history( table_name [, `start-time` [, `end-time` ] ] ) AS h
     [ WHERE h.metadata.id = 'id' ]
     ```
L'*ora di inizio* e l'ora di *fine* sono entrambe opzionali. Sono valori letterali di Amazon Ion che possono essere indicati con backticks (). ``...`` Per ulteriori informazioni, consulta [Interrogazione di Ion con PartiQL in Amazon QLDB](ql-reference.query.md).
Come best practice, qualifica una query cronologica con un intervallo di date (ora di *inizio e ora di fine) e un* *ID del documento ()*. `metadata.id` [QLDB `SELECT` elabora le query nelle transazioni, che sono soggette a un limite di timeout delle transazioni.](limits.md#limits.fixed)  
La cronologia QLDB è indicizzata in base all'ID del documento e al momento non è possibile creare indici di cronologia aggiuntivi. Le interrogazioni cronologiche che includono l'ora di inizio e l'ora di fine ottengono il vantaggio della qualificazione per intervalli di date.

1. Compila ed esegui il `QueryHistory.java` programma per interrogare la cronologia delle revisioni del `VehicleRegistration` documento con VIN. `1N4AL11D75C109151`

Per verificare crittograficamente una revisione del documento nel registro, procedi a. `vehicle-registration` [Passaggio 7: Verificare un documento in un libro mastro](getting-started.java.step-7.md)

# Passaggio 7: Verificare un documento in un libro mastro
Fase 7: Verifica di un documento

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Con Amazon QLDB, puoi verificare in modo efficiente l'integrità di un documento nel diario del tuo libro mastro utilizzando l'hashing crittografico con SHA-256. Per ulteriori informazioni su come funzionano la verifica e l'hashing crittografico in QLDB, consulta. [Verifica dei dati in Amazon QLDB](verification.md)

In questo passaggio, verifichi una revisione del documento nella tabella del `VehicleRegistration` registro. `vehicle-registration` Innanzitutto, richiedi un digest, che viene restituito come file di output e funge da firma dell'intera cronologia delle modifiche del registro. Quindi, richiedi una bozza della revisione relativa a quel digest. Utilizzando questa prova, l'integrità della revisione viene verificata se tutti i controlli di convalida vengono superati.

**Per verificare la revisione di un documento**

1. Esamina i seguenti `.java` file, che rappresentano oggetti QLDB necessari per la verifica e le classi di utilità con metodi di supporto per i valori Ion e string.

   1. `BlockAddress.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.qldb;
      
      import java.util.Objects;
      
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      import com.fasterxml.jackson.annotation.JsonCreator;
      import com.fasterxml.jackson.annotation.JsonProperty;
      
      /**
       * Represents the BlockAddress field of a QLDB document.
       */
      public final class BlockAddress {
      
          private static final Logger log = LoggerFactory.getLogger(BlockAddress.class);
      
          private final String strandId;
          private final long sequenceNo;
      
          @JsonCreator
          public BlockAddress(@JsonProperty("strandId") final String strandId,
                              @JsonProperty("sequenceNo") final long sequenceNo) {
              this.strandId = strandId;
              this.sequenceNo = sequenceNo;
          }
      
          public long getSequenceNo() {
              return sequenceNo;
          }
      
          public String getStrandId() {
              return strandId;
          }
      
          @Override
          public String toString() {
              return "BlockAddress{"
                      + "strandId='" + strandId + '\''
                      + ", sequenceNo=" + sequenceNo
                      + '}';
          }
      
          @Override
          public boolean equals(final Object o) {
              if (this == o) {
                  return true;
              }
              if (o == null || getClass() != o.getClass()) {
                  return false;
              }
              BlockAddress that = (BlockAddress) o;
              return sequenceNo == that.sequenceNo
                      && strandId.equals(that.strandId);
          }
      
          @Override
          public int hashCode() {
              // CHECKSTYLE:OFF - Disabling as we are generating a hashCode of multiple properties.
              return Objects.hash(strandId, sequenceNo);
              // CHECKSTYLE:ON
          }
      }
      ```

   1. `Proof.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.qldb;
      
      import com.amazon.ion.IonReader;
      import com.amazon.ion.IonSystem;
      import com.amazon.ion.system.IonSystemBuilder;
      import com.amazonaws.services.qldb.model.GetRevisionRequest;
      import com.amazonaws.services.qldb.model.GetRevisionResult;
      
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * A Java representation of the {@link Proof} object.
       * Returned from the {@link com.amazonaws.services.qldb.AmazonQLDB#getRevision(GetRevisionRequest)} api.
       */
      public final class Proof {
          private static final IonSystem SYSTEM = IonSystemBuilder.standard().build();
      
          private List<byte[]> internalHashes;
      
          public Proof(final List<byte[]> internalHashes) {
              this.internalHashes = internalHashes;
          }
      
          public List<byte[]> getInternalHashes() {
              return internalHashes;
          }
      
          /**
           * Decodes a {@link Proof} from an ion text String. This ion text is returned in
           * a {@link GetRevisionResult#getProof()}
           *
           * @param ionText
           *              The ion text representing a {@link Proof} object.
           * @return {@link JournalBlock} parsed from the ion text.
           * @throws IllegalStateException if failed to parse the {@link Proof} object from the given ion text.
           */
          public static Proof fromBlob(final String ionText) {
              try {
                  IonReader reader = SYSTEM.newReader(ionText);
                  List<byte[]> list = new ArrayList<>();
                  reader.next();
                  reader.stepIn();
                  while (reader.next() != null) {
                      list.add(reader.newBytes());
                  }
                  return new Proof(list);
              } catch (Exception e) {
                  throw new IllegalStateException("Failed to parse a Proof from byte array");
              }
          }
      }
      ```

   1. `QldbIonUtils.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.qldb;
      
      import com.amazon.ion.IonReader;
      import com.amazon.ion.IonValue;
      import com.amazon.ionhash.IonHashReader;
      import com.amazon.ionhash.IonHashReaderBuilder;
      import com.amazon.ionhash.MessageDigestIonHasherProvider;
      import software.amazon.qldb.tutorial.Constants;
      
      public class QldbIonUtils {
      
          private static MessageDigestIonHasherProvider ionHasherProvider = new MessageDigestIonHasherProvider("SHA-256");
      
          private QldbIonUtils() {}
      
          /**
           * Builds a hash value from the given {@link IonValue}.
           *
           * @param ionValue
           *              The {@link IonValue} to hash.
           * @return a byte array representing the hash value.
           */
          public static byte[] hashIonValue(final IonValue ionValue) {
              IonReader reader = Constants.SYSTEM.newReader(ionValue);
              IonHashReader hashReader = IonHashReaderBuilder.standard()
                      .withHasherProvider(ionHasherProvider)
                      .withReader(reader)
                      .build();
              while (hashReader.next() != null) {  }
              return hashReader.digest();
          }
      
      }
      ```

   1. `QldbStringUtils.java`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial.qldb;
      
      import com.amazon.ion.IonWriter;
      import com.amazon.ion.system.IonReaderBuilder;
      import com.amazon.ion.system.IonTextWriterBuilder;
      import com.amazonaws.services.qldb.model.GetBlockResult;
      import com.amazonaws.services.qldb.model.GetDigestResult;
      import com.amazonaws.services.qldb.model.ValueHolder;
      
      import java.io.IOException;
      
      /**
       * Helper methods to pretty-print certain QLDB response types.
       */
      public class QldbStringUtils {
      
          private QldbStringUtils() {}
      
          /**
           * Returns the string representation of a given {@link ValueHolder}.
           * Adapted from the AWS SDK autogenerated {@code toString()} method, with sensitive values un-redacted.
           * Additionally, this method pretty-prints any IonText included in the {@link ValueHolder}.
           *
           * @param valueHolder the {@link ValueHolder} to convert to a String.
           * @return the String representation of the supplied {@link ValueHolder}.
           */
          public static String toUnredactedString(ValueHolder valueHolder) {
              StringBuilder sb = new StringBuilder();
              sb.append("{");
              if (valueHolder.getIonText() != null) {
      
                  sb.append("IonText: ");
                  IonWriter prettyWriter = IonTextWriterBuilder.pretty().build(sb);
                  try {
                      prettyWriter.writeValues(IonReaderBuilder.standard().build(valueHolder.getIonText()));
                  } catch (IOException ioe) {
                      sb.append("**Exception while printing this IonText**");
                  }
              }
      
              sb.append("}");
              return sb.toString();
          }
      
          /**
           * Returns the string representation of a given {@link GetBlockResult}.
           * Adapted from the AWS SDK autogenerated {@code toString()} method, with sensitive values un-redacted.
           *
           * @param getBlockResult the {@link GetBlockResult} to convert to a String.
           * @return the String representation of the supplied {@link GetBlockResult}.
           */
          public static String toUnredactedString(GetBlockResult getBlockResult) {
              StringBuilder sb = new StringBuilder();
              sb.append("{");
              if (getBlockResult.getBlock() != null) {
                  sb.append("Block: ").append(toUnredactedString(getBlockResult.getBlock())).append(",");
              }
      
              if (getBlockResult.getProof() != null) {
                  sb.append("Proof: ").append(toUnredactedString(getBlockResult.getProof()));
              }
      
              sb.append("}");
              return sb.toString();
          }
      
          /**
           * Returns the string representation of a given {@link GetDigestResult}.
           * Adapted from the AWS SDK autogenerated {@code toString()} method, with sensitive values un-redacted.
           *
           * @param getDigestResult the {@link GetDigestResult} to convert to a String.
           * @return the String representation of the supplied {@link GetDigestResult}.
           */
          public static String toUnredactedString(GetDigestResult getDigestResult) {
              StringBuilder sb = new StringBuilder();
              sb.append("{");
              if (getDigestResult.getDigest() != null) {
                  sb.append("Digest: ").append(getDigestResult.getDigest()).append(",");
              }
      
              if (getDigestResult.getDigestTipAddress() != null) {
                  sb.append("DigestTipAddress: ").append(toUnredactedString(getDigestResult.getDigestTipAddress()));
              }
      
              sb.append("}");
              return sb.toString();
          }
      }
      ```

   1. `Verifier.java`

------
#### [ 2.x ]

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial;
      
      import java.nio.ByteBuffer;
      import java.nio.charset.StandardCharsets;
      import java.security.MessageDigest;
      import java.security.NoSuchAlgorithmException;
      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.Comparator;
      import java.util.Iterator;
      import java.util.List;
      import java.util.concurrent.ThreadLocalRandom;
      
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      import com.amazonaws.util.Base64;
      
      import software.amazon.qldb.tutorial.qldb.Proof;
      
      /**
       * Encapsulates the logic to verify the integrity of revisions or blocks in a QLDB ledger.
       *
       * The main entry point is {@link #verify(byte[], byte[], String)}.
       *
       * This code expects that you have AWS credentials setup per:
       * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
       */
      public final class Verifier {
          public static final Logger log = LoggerFactory.getLogger(Verifier.class);
          private static final int HASH_LENGTH = 32;
          private static final int UPPER_BOUND = 8;
      
          /**
           * Compares two hashes by their <em>signed</em> byte values in little-endian order.
           */
          private static Comparator<byte[]> hashComparator = (h1, h2) -> {
              if (h1.length != HASH_LENGTH || h2.length != HASH_LENGTH) {
                  throw new IllegalArgumentException("Invalid hash.");
              }
              for (int i = h1.length - 1; i >= 0; i--) {
                  int byteEqual = Byte.compare(h1[i], h2[i]);
                  if (byteEqual != 0) {
                      return byteEqual;
                  }
              }
      
              return 0;
          };
      
          private Verifier() { }
      
          /**
           * Verify the integrity of a document with respect to a QLDB ledger digest.
           *
           * The verification algorithm includes the following steps:
           *
           * 1. {@link #buildCandidateDigest(Proof, byte[])} build the candidate digest from the internal hashes
           * in the {@link Proof}.
           * 2. Check that the {@code candidateLedgerDigest} is equal to the {@code ledgerDigest}.
           *
           * @param documentHash
           *              The hash of the document to be verified.
           * @param digest
           *              The QLDB ledger digest. This digest should have been retrieved using
           *              {@link com.amazonaws.services.qldb.AmazonQLDB#getDigest}
           * @param proofBlob
           *              The ion encoded bytes representing the {@link Proof} associated with the supplied
           *              {@code digestTipAddress} and {@code address} retrieved using
           *              {@link com.amazonaws.services.qldb.AmazonQLDB#getRevision}.
           * @return {@code true} if the record is verified or {@code false} if it is not verified.
           */
          public static boolean verify(
                  final byte[] documentHash,
                  final byte[] digest,
                  final String proofBlob
          ) {
              Proof proof = Proof.fromBlob(proofBlob);
      
              byte[] candidateDigest = buildCandidateDigest(proof, documentHash);
      
              return Arrays.equals(digest, candidateDigest);
          }
      
          /**
           * Build the candidate digest representing the entire ledger from the internal hashes of the {@link Proof}.
           *
           * @param proof
           *              A Java representation of {@link Proof}
           *              returned from {@link com.amazonaws.services.qldb.AmazonQLDB#getRevision}.
           * @param leafHash
           *              Leaf hash to build the candidate digest with.
           * @return a byte array of the candidate digest.
           */
          private static byte[] buildCandidateDigest(final Proof proof, final byte[] leafHash) {
              return calculateRootHashFromInternalHashes(proof.getInternalHashes(), leafHash);
          }
      
          /**
           * Get a new instance of {@link MessageDigest} using the SHA-256 algorithm.
           *
           * @return an instance of {@link MessageDigest}.
           * @throws IllegalStateException if the algorithm is not available on the current JVM.
           */
          static MessageDigest newMessageDigest() {
              try {
                  return MessageDigest.getInstance("SHA-256");
              } catch (NoSuchAlgorithmException e) {
                  log.error("Failed to create SHA-256 MessageDigest", e);
                  throw new IllegalStateException("SHA-256 message digest is unavailable", e);
              }
          }
      
          /**
           * Takes two hashes, sorts them, concatenates them, and then returns the
           * hash of the concatenated array.
           *
           * @param h1
           *              Byte array containing one of the hashes to compare.
           * @param h2
           *              Byte array containing one of the hashes to compare.
           * @return the concatenated array of hashes.
           */
          public static byte[] dot(final byte[] h1, final byte[] h2) {
              if (h1.length == 0) {
                  return h2;
              }
              if (h2.length == 0) {
                  return h1;
              }
              byte[] concatenated = new byte[h1.length + h2.length];
              if (hashComparator.compare(h1, h2) < 0) {
                  System.arraycopy(h1, 0, concatenated, 0, h1.length);
                  System.arraycopy(h2, 0, concatenated, h1.length, h2.length);
              } else {
                  System.arraycopy(h2, 0, concatenated, 0, h2.length);
                  System.arraycopy(h1, 0, concatenated, h2.length, h1.length);
              }
              MessageDigest messageDigest = newMessageDigest();
              messageDigest.update(concatenated);
      
              return messageDigest.digest();
          }
      
          /**
           * Starting with the provided {@code leafHash} combined with the provided {@code internalHashes}
           * pairwise until only the root hash remains.
           *
           * @param internalHashes
           *              Internal hashes of Merkle tree.
           * @param leafHash
           *              Leaf hashes of Merkle tree.
           * @return the root hash.
           */
          private static byte[] calculateRootHashFromInternalHashes(final List<byte[]> internalHashes, final byte[] leafHash) {
              return internalHashes.stream().reduce(leafHash, Verifier::dot);
          }
      
          /**
           * Flip a single random bit in the given byte array. This method is used to demonstrate
           * QLDB's verification features.
           *
           * @param original
           *              The original byte array.
           * @return the altered byte array with a single random bit changed.
           */
          public static byte[] flipRandomBit(final byte[] original) {
              if (original.length == 0) {
                  throw new IllegalArgumentException("Array cannot be empty!");
              }
              int alteredPosition = ThreadLocalRandom.current().nextInt(original.length);
              int b = ThreadLocalRandom.current().nextInt(UPPER_BOUND);
              byte[] altered = new byte[original.length];
              System.arraycopy(original, 0, altered, 0, original.length);
              altered[alteredPosition] = (byte) (altered[alteredPosition] ^ (1 << b));
              return altered;
          }
      
          public static String toBase64(byte[] arr) {
              return new String(Base64.encode(arr), StandardCharsets.UTF_8);
          }
      
          /**
           * Convert a {@link ByteBuffer} into byte array.
           *
           * @param buffer
           *              The {@link ByteBuffer} to convert.
           * @return the converted byte array.
           */
          public static byte[] convertByteBufferToByteArray(final ByteBuffer buffer) {
              byte[] arr = new byte[buffer.remaining()];
              buffer.get(arr);
              return arr;
          }
      
          /**
           * Calculates the root hash from a list of hashes that represent the base of a Merkle tree.
           *
           * @param hashes
           *              The list of byte arrays representing hashes making up base of a Merkle tree.
           * @return a byte array that is the root hash of the given list of hashes.
           */
          public static byte[] calculateMerkleTreeRootHash(List<byte[]> hashes) {
              if (hashes.isEmpty()) {
                  return new byte[0];
              }
      
              List<byte[]> remaining = combineLeafHashes(hashes);
              while (remaining.size() > 1) {
                  remaining = combineLeafHashes(remaining);
              }
              return remaining.get(0);
          }
      
          private static List<byte[]> combineLeafHashes(List<byte[]> hashes) {
              List<byte[]> combinedHashes = new ArrayList<>();
              Iterator<byte[]> it = hashes.stream().iterator();
      
              while (it.hasNext()) {
                  byte[] left = it.next();
                  if (it.hasNext()) {
                      byte[] right = it.next();
                      byte[] combined = dot(left, right);
                      combinedHashes.add(combined);
                  } else {
                      combinedHashes.add(left);
                  }
              }
      
              return combinedHashes;
          }
      }
      ```

------
#### [ 1.x ]

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      package software.amazon.qldb.tutorial;
      
      import com.amazonaws.util.Base64;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import software.amazon.qldb.tutorial.qldb.Proof;
      
      import java.nio.ByteBuffer;
      import java.nio.charset.StandardCharsets;
      import java.security.MessageDigest;
      import java.security.NoSuchAlgorithmException;
      import java.util.*;
      import java.util.concurrent.ThreadLocalRandom;
      
      /**
       * Encapsulates the logic to verify the integrity of revisions or blocks in a QLDB ledger.
       *
       * The main entry point is {@link #verify(byte[], byte[], String)}.
       *
       * This code expects that you have AWS credentials setup per:
       * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
       */
      public final class Verifier {
          public static final Logger log = LoggerFactory.getLogger(Verifier.class);
          private static final int HASH_LENGTH = 32;
          private static final int UPPER_BOUND = 8;
      
          /**
           * Compares two hashes by their <em>signed</em> byte values in little-endian order.
           */
          private static Comparator<byte[]> hashComparator = (h1, h2) -> {
              if (h1.length != HASH_LENGTH || h2.length != HASH_LENGTH) {
                  throw new IllegalArgumentException("Invalid hash.");
              }
              for (int i = h1.length - 1; i >= 0; i--) {
                  int byteEqual = Byte.compare(h1[i], h2[i]);
                  if (byteEqual != 0) {
                      return byteEqual;
                  }
              }
      
              return 0;
          };
      
          private Verifier() { }
      
          /**
           * Verify the integrity of a document with respect to a QLDB ledger digest.
           *
           * The verification algorithm includes the following steps:
           *
           * 1. {@link #buildCandidateDigest(Proof, byte[])} build the candidate digest from the internal hashes
           * in the {@link Proof}.
           * 2. Check that the {@code candidateLedgerDigest} is equal to the {@code ledgerDigest}.
           *
           * @param documentHash
           *              The hash of the document to be verified.
           * @param digest
           *              The QLDB ledger digest. This digest should have been retrieved using
           *              {@link com.amazonaws.services.qldb.AmazonQLDB#getDigest}
           * @param proofBlob
           *              The ion encoded bytes representing the {@link Proof} associated with the supplied
           *              {@code digestTipAddress} and {@code address} retrieved using
           *              {@link com.amazonaws.services.qldb.AmazonQLDB#getRevision}.
           * @return {@code true} if the record is verified or {@code false} if it is not verified.
           */
          public static boolean verify(
                  final byte[] documentHash,
                  final byte[] digest,
                  final String proofBlob
          ) {
              Proof proof = Proof.fromBlob(proofBlob);
      
              byte[] candidateDigest = buildCandidateDigest(proof, documentHash);
      
              return Arrays.equals(digest, candidateDigest);
          }
      
          /**
           * Build the candidate digest representing the entire ledger from the internal hashes of the {@link Proof}.
           *
           * @param proof
           *              A Java representation of {@link Proof}
           *              returned from {@link com.amazonaws.services.qldb.AmazonQLDB#getRevision}.
           * @param leafHash
           *              Leaf hash to build the candidate digest with.
           * @return a byte array of the candidate digest.
           */
          private static byte[] buildCandidateDigest(final Proof proof, final byte[] leafHash) {
              return calculateRootHashFromInternalHashes(proof.getInternalHashes(), leafHash);
          }
      
          /**
           * Get a new instance of {@link MessageDigest} using the SHA-256 algorithm.
           *
           * @return an instance of {@link MessageDigest}.
           * @throws IllegalStateException if the algorithm is not available on the current JVM.
           */
          static MessageDigest newMessageDigest() {
              try {
                  return MessageDigest.getInstance("SHA-256");
              } catch (NoSuchAlgorithmException e) {
                  log.error("Failed to create SHA-256 MessageDigest", e);
                  throw new IllegalStateException("SHA-256 message digest is unavailable", e);
              }
          }
      
          /**
           * Takes two hashes, sorts them, concatenates them, and then returns the
           * hash of the concatenated array.
           *
           * @param h1
           *              Byte array containing one of the hashes to compare.
           * @param h2
           *              Byte array containing one of the hashes to compare.
           * @return the concatenated array of hashes.
           */
          public static byte[] dot(final byte[] h1, final byte[] h2) {
              if (h1.length == 0) {
                  return h2;
              }
              if (h2.length == 0) {
                  return h1;
              }
              byte[] concatenated = new byte[h1.length + h2.length];
              if (hashComparator.compare(h1, h2) < 0) {
                  System.arraycopy(h1, 0, concatenated, 0, h1.length);
                  System.arraycopy(h2, 0, concatenated, h1.length, h2.length);
              } else {
                  System.arraycopy(h2, 0, concatenated, 0, h2.length);
                  System.arraycopy(h1, 0, concatenated, h2.length, h1.length);
              }
              MessageDigest messageDigest = newMessageDigest();
              messageDigest.update(concatenated);
      
              return messageDigest.digest();
          }
      
          /**
           * Starting with the provided {@code leafHash} combined with the provided {@code internalHashes}
           * pairwise until only the root hash remains.
           *
           * @param internalHashes
           *              Internal hashes of Merkle tree.
           * @param leafHash
           *              Leaf hashes of Merkle tree.
           * @return the root hash.
           */
          private static byte[] calculateRootHashFromInternalHashes(final List<byte[]> internalHashes, final byte[] leafHash) {
              return internalHashes.stream().reduce(leafHash, Verifier::dot);
          }
      
          /**
           * Flip a single random bit in the given byte array. This method is used to demonstrate
           * QLDB's verification features.
           *
           * @param original
           *              The original byte array.
           * @return the altered byte array with a single random bit changed.
           */
          public static byte[] flipRandomBit(final byte[] original) {
              if (original.length == 0) {
                  throw new IllegalArgumentException("Array cannot be empty!");
              }
              int alteredPosition = ThreadLocalRandom.current().nextInt(original.length);
              int b = ThreadLocalRandom.current().nextInt(UPPER_BOUND);
              byte[] altered = new byte[original.length];
              System.arraycopy(original, 0, altered, 0, original.length);
              altered[alteredPosition] = (byte) (altered[alteredPosition] ^ (1 << b));
              return altered;
          }
      
          public static String toBase64(byte[] arr) {
              return new String(Base64.encode(arr), StandardCharsets.UTF_8);
          }
      
          /**
           * Convert a {@link ByteBuffer} into byte array.
           *
           * @param buffer
           *              The {@link ByteBuffer} to convert.
           * @return the converted byte array.
           */
          public static byte[] convertByteBufferToByteArray(final ByteBuffer buffer) {
              byte[] arr = new byte[buffer.remaining()];
              buffer.get(arr);
              return arr;
          }
      
          /**
           * Calculates the root hash from a list of hashes that represent the base of a Merkle tree.
           *
           * @param hashes
           *              The list of byte arrays representing hashes making up base of a Merkle tree.
           * @return a byte array that is the root hash of the given list of hashes.
           */
          public static byte[] calculateMerkleTreeRootHash(List<byte[]> hashes) {
              if (hashes.isEmpty()) {
                  return new byte[0];
              }
      
              List<byte[]> remaining = combineLeafHashes(hashes);
              while (remaining.size() > 1) {
                  remaining = combineLeafHashes(remaining);
              }
              return remaining.get(0);
          }
      
          private static List<byte[]> combineLeafHashes(List<byte[]> hashes) {
              List<byte[]> combinedHashes = new ArrayList<>();
              Iterator<byte[]> it = hashes.stream().iterator();
      
              while (it.hasNext()) {
                  byte[] left = it.next();
                  if (it.hasNext()) {
                      byte[] right = it.next();
                      byte[] combined = dot(left, right);
                      combinedHashes.add(combined);
                  } else {
                      combinedHashes.add(left);
                  }
              }
      
              return combinedHashes;
          }
      }
      ```

------

1. Utilizzate due `.java` file (`GetDigest.java`e`GetRevision.java`) per eseguire le seguenti operazioni:
   + Richiedi un nuovo digest dal `vehicle-registration` registro.
   + Richiedi una bozza per ogni revisione di un documento dalla tabella. `VehicleRegistration`
   + Verifica le revisioni utilizzando il digest restituito e verifica ricalcolando il digest.

   Il `GetDigest.java` programma contiene il codice seguente.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazonaws.services.qldb.AmazonQLDB;
   import com.amazonaws.services.qldb.model.GetDigestRequest;
   import com.amazonaws.services.qldb.model.GetDigestResult;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   import software.amazon.qldb.tutorial.qldb.QldbStringUtils;
   
   /**
    * This is an example for retrieving the digest of a particular ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class GetDigest {
       public static final Logger log = LoggerFactory.getLogger(GetDigest.class);
       public static AmazonQLDB client = CreateLedger.getClient();
   
       private GetDigest() { }
   
       /**
        * Calls {@link #getDigest(String)} for a ledger.
        *
        * @param args
        *              Arbitrary command-line arguments.
        * @throws Exception if failed to get a ledger digest.
        */
       public static void main(final String... args) throws Exception {
           try {
   
               getDigest(Constants.LEDGER_NAME);
   
           } catch (Exception e) {
               log.error("Unable to get a ledger digest!", e);
               throw e;
           }
       }
   
       /**
        * Get the digest for the specified ledger.
        *
        * @param ledgerName
        *              The ledger to get digest from.
        * @return {@link GetDigestResult}.
        */
       public static GetDigestResult getDigest(final String ledgerName) {
           log.info("Let's get the current digest of the ledger named {}.", ledgerName);
           GetDigestRequest request = new GetDigestRequest()
                   .withName(ledgerName);
           GetDigestResult result = client.getDigest(request);
           log.info("Success. LedgerDigest: {}.", QldbStringUtils.toUnredactedString(result));
           return result;
       }
   }
   ```
**Nota**  
Usa il `getDigest` metodo per richiedere un riassunto che copra la *punta* corrente del diario nel tuo libro mastro. Il tip of the journal si riferisce all'ultimo blocco eseguito nel momento in cui QLDB riceve la tua richiesta.

   Il `GetRevision.java` programma contiene il seguente codice.

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazon.ion.IonReader;
   import com.amazon.ion.IonStruct;
   import com.amazon.ion.IonSystem;
   import com.amazon.ion.IonValue;
   import com.amazon.ion.IonWriter;
   import com.amazon.ion.system.IonReaderBuilder;
   import com.amazon.ion.system.IonSystemBuilder;
   import com.amazonaws.services.qldb.AmazonQLDB;
   import com.amazonaws.services.qldb.model.GetDigestResult;
   import com.amazonaws.services.qldb.model.GetRevisionRequest;
   import com.amazonaws.services.qldb.model.GetRevisionResult;
   import com.amazonaws.services.qldb.model.ValueHolder;
   import java.io.ByteArrayOutputStream;
   import java.io.IOException;
   import java.util.ArrayList;
   import java.util.Collections;
   import java.util.List;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   import software.amazon.qldb.QldbDriver;
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.SampleData;
   import software.amazon.qldb.tutorial.qldb.BlockAddress;
   import software.amazon.qldb.tutorial.qldb.QldbRevision;
   import software.amazon.qldb.tutorial.qldb.QldbStringUtils;
   
   /**
    * Verify the integrity of a document revision in a QLDB ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class GetRevision {
       public static final Logger log = LoggerFactory.getLogger(GetRevision.class);
       public static AmazonQLDB client = CreateLedger.getClient();
       private static final IonSystem SYSTEM = IonSystemBuilder.standard().build();
   
       private GetRevision() { }
   
       public static void main(String... args) throws Exception {
   
           final String vin = SampleData.REGISTRATIONS.get(0).getVin();
   
   
           verifyRegistration(ConnectToLedger.getDriver(), Constants.LEDGER_NAME, vin);
       }
   
       /**
        * Verify each version of the registration for the given VIN.
        *
        * @param driver
        *              A QLDB driver.
        * @param ledgerName
        *              The ledger to get digest from.
        * @param vin
        *              VIN to query the revision history of a specific registration with.
        * @throws Exception if failed to verify digests.
        * @throws AssertionError if document revision verification failed.
        */
       public static void verifyRegistration(final QldbDriver driver, final String ledgerName, final String vin)
               throws Exception {
           log.info(String.format("Let's verify the registration with VIN=%s, in ledger=%s.", vin, ledgerName));
   
           try {
               log.info("First, let's get a digest.");
               GetDigestResult digestResult = GetDigest.getDigest(ledgerName);
   
               ValueHolder digestTipAddress = digestResult.getDigestTipAddress();
               byte[] digestBytes = Verifier.convertByteBufferToByteArray(digestResult.getDigest());
   
               log.info("Got a ledger digest. Digest end address={}, digest={}.",
                   QldbStringUtils.toUnredactedString(digestTipAddress),
                   Verifier.toBase64(digestBytes));
   
               log.info(String.format("Next, let's query the registration with VIN=%s. "
                       + "Then we can verify each version of the registration.", vin));
               List<IonStruct> documentsWithMetadataList = new ArrayList<>();
               driver.execute(txn -> {
                   documentsWithMetadataList.addAll(queryRegistrationsByVin(txn, vin));
               });
               log.info("Registrations queried successfully!");
   
               log.info(String.format("Found %s revisions of the registration with VIN=%s.",
                       documentsWithMetadataList.size(), vin));
   
               for (IonStruct ionStruct : documentsWithMetadataList) {
   
                   QldbRevision document = QldbRevision.fromIon(ionStruct);
                   log.info(String.format("Let's verify the document: %s", document));
   
                   log.info("Let's get a proof for the document.");
                   GetRevisionResult proofResult = getRevision(
                           ledgerName,
                           document.getMetadata().getId(),
                           digestTipAddress,
                           document.getBlockAddress()
                   );
   
                   final IonValue proof = Constants.MAPPER.writeValueAsIonValue(proofResult.getProof());
                   final IonReader reader = IonReaderBuilder.standard().build(proof);
                   reader.next();
                   ByteArrayOutputStream baos = new ByteArrayOutputStream();
                   IonWriter writer = SYSTEM.newBinaryWriter(baos);
                   writer.writeValue(reader);
                   writer.close();
                   baos.flush();
                   baos.close();
                   byte[] byteProof = baos.toByteArray();
   
                   log.info(String.format("Got back a proof: %s", Verifier.toBase64(byteProof)));
   
                   boolean verified = Verifier.verify(
                           document.getHash(),
                           digestBytes,
                           proofResult.getProof().getIonText()
                   );
   
                   if (!verified) {
                       throw new AssertionError("Document revision is not verified!");
                   } else {
                       log.info("Success! The document is verified");
                   }
   
                   byte[] alteredDigest = Verifier.flipRandomBit(digestBytes);
                   log.info(String.format("Flipping one bit in the digest and assert that the document is NOT verified. "
                           + "The altered digest is: %s", Verifier.toBase64(alteredDigest)));
                   verified = Verifier.verify(
                           document.getHash(),
                           alteredDigest,
                           proofResult.getProof().getIonText()
                   );
   
                   if (verified) {
                       throw new AssertionError("Expected document to not be verified against altered digest.");
                   } else {
                       log.info("Success! As expected flipping a bit in the digest causes verification to fail.");
                   }
   
                   byte[] alteredDocumentHash = Verifier.flipRandomBit(document.getHash());
                   log.info(String.format("Flipping one bit in the document's hash and assert that it is NOT verified. "
                           + "The altered document hash is: %s.", Verifier.toBase64(alteredDocumentHash)));
                   verified = Verifier.verify(
                           alteredDocumentHash,
                           digestBytes,
                           proofResult.getProof().getIonText()
                   );
   
                   if (verified) {
                       throw new AssertionError("Expected altered document hash to not be verified against digest.");
                   } else {
                       log.info("Success! As expected flipping a bit in the document hash causes verification to fail.");
                   }
               }
   
           } catch (Exception e) {
               log.error("Failed to verify digests.", e);
               throw e;
           }
   
           log.info(String.format("Finished verifying the registration with VIN=%s in ledger=%s.", vin, ledgerName));
       }
   
       /**
        * Get the revision of a particular document specified by the given document ID and block address.
        *
        * @param ledgerName
        *              Name of the ledger containing the document.
        * @param documentId
        *              Unique ID for the document to be verified, contained in the committed view of the document.
        * @param digestTipAddress
        *              The latest block location covered by the digest.
        * @param blockAddress
        *              The location of the block to request.
        * @return the requested revision.
        */
       public static GetRevisionResult getRevision(final String ledgerName, final String documentId,
                                                   final ValueHolder digestTipAddress, final BlockAddress blockAddress) {
           try {
               GetRevisionRequest request = new GetRevisionRequest()
                       .withName(ledgerName)
                       .withDigestTipAddress(digestTipAddress)
                       .withBlockAddress(new ValueHolder().withIonText(Constants.MAPPER.writeValueAsIonValue(blockAddress)
                               .toString()))
                       .withDocumentId(documentId);
               return client.getRevision(request);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Query the registration history for the given VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              The unique VIN to query.
        * @return a list of {@link IonStruct} representing the registration history.
        * @throws IllegalStateException if failed to convert parameters into {@link IonValue}
        */
       public static List<IonStruct> queryRegistrationsByVin(final TransactionExecutor txn, final String vin) {
           log.info(String.format("Let's query the 'VehicleRegistration' table for VIN: %s...", vin));
           log.info("Let's query the 'VehicleRegistration' table for VIN: {}...", vin);
           final String query = String.format("SELECT * FROM _ql_committed_%s WHERE data.VIN = ?",
                   Constants.VEHICLE_REGISTRATION_TABLE_NAME);
           try {
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(vin));
               final Result result = txn.execute(query, parameters);
               List<IonStruct> list = ScanTable.toIonStructs(result);
               log.info(String.format("Found %d document(s)!", list.size()));
               return list;
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   }
   ```

------
#### [ 1.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazon.ion.IonReader;
   import com.amazon.ion.IonStruct;
   import com.amazon.ion.IonValue;
   import com.amazon.ion.IonWriter;
   import com.amazon.ion.system.IonReaderBuilder;
   import com.amazonaws.services.qldb.AmazonQLDB;
   import com.amazonaws.services.qldb.model.GetDigestResult;
   import com.amazonaws.services.qldb.model.GetRevisionRequest;
   import com.amazonaws.services.qldb.model.GetRevisionResult;
   import com.amazonaws.services.qldb.model.ValueHolder;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   import software.amazon.qldb.QldbSession;
   import software.amazon.qldb.Result;
   import software.amazon.qldb.TransactionExecutor;
   import software.amazon.qldb.tutorial.model.SampleData;
   import software.amazon.qldb.tutorial.qldb.BlockAddress;
   import software.amazon.qldb.tutorial.qldb.QldbRevision;
   import software.amazon.qldb.tutorial.qldb.QldbStringUtils;
   
   import java.io.ByteArrayOutputStream;
   import java.io.IOException;
   import java.util.ArrayList;
   import java.util.Collections;
   import java.util.List;
   
   /**
    * Verify the integrity of a document revision in a QLDB ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class GetRevision {
       public static final Logger log = LoggerFactory.getLogger(GetRevision.class);
       public static AmazonQLDB client = CreateLedger.getClient();
   
       private GetRevision() { }
   
       public static void main(String... args) throws Exception {
   
           final String vin = SampleData.REGISTRATIONS.get(0).getVin();
   
           try (QldbSession qldbSession = ConnectToLedger.createQldbSession()) {
               verifyRegistration(qldbSession, Constants.LEDGER_NAME, vin);
           }
       }
   
       /**
        * Verify each version of the registration for the given VIN.
        *
        * @param qldbSession
        *              A QLDB session.
        * @param ledgerName
        *              The ledger to get digest from.
        * @param vin
        *              VIN to query the revision history of a specific registration with.
        * @throws Exception if failed to verify digests.
        * @throws AssertionError if document revision verification failed.
        */
       public static void verifyRegistration(final QldbSession qldbSession, final String ledgerName, final String vin)
               throws Exception {
           log.info(String.format("Let's verify the registration with VIN=%s, in ledger=%s.", vin, ledgerName));
   
           try {
               log.info("First, let's get a digest.");
               GetDigestResult digestResult = GetDigest.getDigest(ledgerName);
   
               ValueHolder digestTipAddress = digestResult.getDigestTipAddress();
               byte[] digestBytes = Verifier.convertByteBufferToByteArray(digestResult.getDigest());
   
               log.info("Got a ledger digest. Digest end address={}, digest={}.",
                   QldbStringUtils.toUnredactedString(digestTipAddress),
                   Verifier.toBase64(digestBytes));
   
               log.info(String.format("Next, let's query the registration with VIN=%s. "
                       + "Then we can verify each version of the registration.", vin));
               List<IonStruct> documentsWithMetadataList = new ArrayList<>();
               qldbSession.execute(txn -> {
                   documentsWithMetadataList.addAll(queryRegistrationsByVin(txn, vin));
               }, (retryAttempt) -> log.info("Retrying due to OCC conflict..."));
               log.info("Registrations queried successfully!");
   
               log.info(String.format("Found %s revisions of the registration with VIN=%s.",
                       documentsWithMetadataList.size(), vin));
   
               for (IonStruct ionStruct : documentsWithMetadataList) {
   
                   QldbRevision document = QldbRevision.fromIon(ionStruct);
                   log.info(String.format("Let's verify the document: %s", document));
   
                   log.info("Let's get a proof for the document.");
                   GetRevisionResult proofResult = getRevision(
                           ledgerName,
                           document.getMetadata().getId(),
                           digestTipAddress,
                           document.getBlockAddress()
                   );
   
                   final IonValue proof = Constants.MAPPER.writeValueAsIonValue(proofResult.getProof());
                   final IonReader reader = IonReaderBuilder.standard().build(proof);
                   reader.next();
                   ByteArrayOutputStream baos = new ByteArrayOutputStream();
                   IonWriter writer = Constants.SYSTEM.newBinaryWriter(baos);
                   writer.writeValue(reader);
                   writer.close();
                   baos.flush();
                   baos.close();
                   byte[] byteProof = baos.toByteArray();
   
                   log.info(String.format("Got back a proof: %s", Verifier.toBase64(byteProof)));
   
                   boolean verified = Verifier.verify(
                           document.getHash(),
                           digestBytes,
                           proofResult.getProof().getIonText()
                   );
   
                   if (!verified) {
                       throw new AssertionError("Document revision is not verified!");
                   } else {
                       log.info("Success! The document is verified");
                   }
   
                   byte[] alteredDigest = Verifier.flipRandomBit(digestBytes);
                   log.info(String.format("Flipping one bit in the digest and assert that the document is NOT verified. "
                           + "The altered digest is: %s", Verifier.toBase64(alteredDigest)));
                   verified = Verifier.verify(
                           document.getHash(),
                           alteredDigest,
                           proofResult.getProof().getIonText()
                   );
   
                   if (verified) {
                       throw new AssertionError("Expected document to not be verified against altered digest.");
                   } else {
                       log.info("Success! As expected flipping a bit in the digest causes verification to fail.");
                   }
   
                   byte[] alteredDocumentHash = Verifier.flipRandomBit(document.getHash());
                   log.info(String.format("Flipping one bit in the document's hash and assert that it is NOT verified. "
                           + "The altered document hash is: %s.", Verifier.toBase64(alteredDocumentHash)));
                   verified = Verifier.verify(
                           alteredDocumentHash,
                           digestBytes,
                           proofResult.getProof().getIonText()
                   );
   
                   if (verified) {
                       throw new AssertionError("Expected altered document hash to not be verified against digest.");
                   } else {
                       log.info("Success! As expected flipping a bit in the document hash causes verification to fail.");
                   }
               }
   
           } catch (Exception e) {
               log.error("Failed to verify digests.", e);
               throw e;
           }
   
           log.info(String.format("Finished verifying the registration with VIN=%s in ledger=%s.", vin, ledgerName));
       }
   
       /**
        * Get the revision of a particular document specified by the given document ID and block address.
        *
        * @param ledgerName
        *              Name of the ledger containing the document.
        * @param documentId
        *              Unique ID for the document to be verified, contained in the committed view of the document.
        * @param digestTipAddress
        *              The latest block location covered by the digest.
        * @param blockAddress
        *              The location of the block to request.
        * @return the requested revision.
        */
       public static GetRevisionResult getRevision(final String ledgerName, final String documentId,
                                                   final ValueHolder digestTipAddress, final BlockAddress blockAddress) {
           try {
               GetRevisionRequest request = new GetRevisionRequest()
                       .withName(ledgerName)
                       .withDigestTipAddress(digestTipAddress)
                       .withBlockAddress(new ValueHolder().withIonText(Constants.MAPPER.writeValueAsIonValue(blockAddress)
                               .toString()))
                       .withDocumentId(documentId);
               return client.getRevision(request);
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   
       /**
        * Query the registration history for the given VIN.
        *
        * @param txn
        *              The {@link TransactionExecutor} for lambda execute.
        * @param vin
        *              The unique VIN to query.
        * @return a list of {@link IonStruct} representing the registration history.
        * @throws IllegalStateException if failed to convert parameters into {@link IonValue}
        */
       public static List<IonStruct> queryRegistrationsByVin(final TransactionExecutor txn, final String vin) {
           log.info(String.format("Let's query the 'VehicleRegistration' table for VIN: %s...", vin));
           log.info("Let's query the 'VehicleRegistration' table for VIN: {}...", vin);
           final String query = String.format("SELECT * FROM _ql_committed_%s WHERE data.VIN = ?",
                   Constants.VEHICLE_REGISTRATION_TABLE_NAME);
           try {
               final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(vin));
               final Result result = txn.execute(query, parameters);
               List<IonStruct> list = ScanTable.toIonStructs(result);
               log.info(String.format("Found %d document(s)!", list.size()));
               return list;
           } catch (IOException ioe) {
               throw new IllegalStateException(ioe);
           }
       }
   }
   ```

------
**Nota**  
Dopo che il `getRevision` metodo ha restituito una bozza per la revisione del documento specificata, questo programma utilizza un'API lato client per verificare tale revisione. Per una panoramica dell'algoritmo utilizzato da questa API, consulta. [Utilizzo di una bozza per ricalcolare il riassunto](verification.results.md#verification.results.recalc)

1. Compila ed esegui il `GetRevision.java` programma per verificare crittograficamente il `VehicleRegistration` documento con VIN. `1N4AL11D75C109151`

Per esportare e convalidare i dati del giornale nel `vehicle-registration` libro mastro, procedi a. [Fase 8: Esportazione e convalida dei dati del diario in un libro mastro](getting-started.java.step-8.md)

# Fase 8: Esportazione e convalida dei dati del diario in un libro mastro
Fase 8: Esportazione e convalida dei dati del diario

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In Amazon QLDB, puoi accedere ai contenuti del diario nel tuo registro per vari scopi, come la conservazione dei dati, l'analisi e il controllo. Per ulteriori informazioni, consulta [Esportazione dei dati del diario da Amazon QLDB](export-journal.md).

In questo passaggio, esporti i [blocchi di journal](journal-contents.md) dal `vehicle-registration` registro in un bucket Amazon S3. Quindi, usi i dati esportati per convalidare la catena di hash tra i blocchi di journal e i singoli componenti hash all'interno di ogni blocco.

L'entità principale AWS Identity and Access Management (IAM) che utilizzi deve disporre di autorizzazioni IAM sufficienti per creare un bucket Amazon S3 nel tuo. Account AWS Per informazioni, consulta [Policies and Permissions in Amazon S3 nella Amazon](https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-policy-language-overview.html) S3 *User Guide*. È inoltre necessario disporre delle autorizzazioni per creare un ruolo IAM con una politica di autorizzazioni allegata che consenta a QLDB di scrivere oggetti nel bucket Amazon S3. *Per ulteriori informazioni, consulta [Autorizzazioni necessarie per accedere alle risorse IAM nella Guida per l'utente IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_permissions-required.html).*

**Per esportare e convalidare i dati del diario**

1. Esamina il seguente file (`JournalBlock.java`), che rappresenta un blocco di journal e il relativo contenuto di dati. Include un metodo denominato `verifyBlockHash()` che dimostra come calcolare ogni singolo componente di un block hash.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial.qldb;
   
   import com.fasterxml.jackson.annotation.JsonCreator;
   import com.fasterxml.jackson.annotation.JsonProperty;
   import com.fasterxml.jackson.databind.annotation.JsonSerialize;
   import com.fasterxml.jackson.dataformat.ion.IonTimestampSerializers;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   import software.amazon.qldb.tutorial.Constants;
   import software.amazon.qldb.tutorial.Verifier;
   
   import java.io.IOException;
   import java.nio.ByteBuffer;
   import java.util.Arrays;
   import java.util.Date;
   import java.util.HashSet;
   import java.util.List;
   import java.util.Set;
   import java.util.stream.Collectors;
   
   import static java.nio.ByteBuffer.wrap;
   
   /**
    * Represents a JournalBlock that was recorded after executing a transaction
    * in the ledger.
    */
   public final class JournalBlock {
       private static final Logger log = LoggerFactory.getLogger(JournalBlock.class);
   
       private BlockAddress blockAddress;
       private String transactionId;
       @JsonSerialize(using = IonTimestampSerializers.IonTimestampJavaDateSerializer.class)
       private Date blockTimestamp;
       private byte[] blockHash;
       private byte[] entriesHash;
       private byte[] previousBlockHash;
       private byte[][] entriesHashList;
       private TransactionInfo transactionInfo;
       private RedactionInfo redactionInfo;
       private List<QldbRevision> revisions;
   
       @JsonCreator
       public JournalBlock(@JsonProperty("blockAddress") final BlockAddress blockAddress,
                           @JsonProperty("transactionId") final String transactionId,
                           @JsonProperty("blockTimestamp") final Date blockTimestamp,
                           @JsonProperty("blockHash") final byte[] blockHash,
                           @JsonProperty("entriesHash") final byte[] entriesHash,
                           @JsonProperty("previousBlockHash") final byte[] previousBlockHash,
                           @JsonProperty("entriesHashList") final byte[][] entriesHashList,
                           @JsonProperty("transactionInfo") final TransactionInfo transactionInfo,
                           @JsonProperty("redactionInfo") final RedactionInfo redactionInfo,
                           @JsonProperty("revisions") final List<QldbRevision> revisions) {
           this.blockAddress = blockAddress;
           this.transactionId = transactionId;
           this.blockTimestamp = blockTimestamp;
           this.blockHash = blockHash;
           this.entriesHash = entriesHash;
           this.previousBlockHash = previousBlockHash;
           this.entriesHashList = entriesHashList;
           this.transactionInfo = transactionInfo;
           this.redactionInfo = redactionInfo;
           this.revisions = revisions;
       }
   
       public BlockAddress getBlockAddress() {
           return blockAddress;
       }
   
       public String getTransactionId() {
           return transactionId;
       }
   
       public Date getBlockTimestamp() {
           return blockTimestamp;
       }
   
       public byte[][] getEntriesHashList() {
           return entriesHashList;
       }
   
       public TransactionInfo getTransactionInfo() {
           return transactionInfo;
       }
   
       public RedactionInfo getRedactionInfo() {
           return redactionInfo;
       }
   
       public List<QldbRevision> getRevisions() {
           return revisions;
       }
   
       public byte[] getEntriesHash() {
           return entriesHash;
       }
   
       public byte[] getBlockHash() {
           return blockHash;
       }
   
       public byte[] getPreviousBlockHash() {
           return previousBlockHash;
       }
   
       @Override
       public String toString() {
           return "JournalBlock{"
                   + "blockAddress=" + blockAddress
                   + ", transactionId='" + transactionId + '\''
                   + ", blockTimestamp=" + blockTimestamp
                   + ", blockHash=" + Arrays.toString(blockHash)
                   + ", entriesHash=" + Arrays.toString(entriesHash)
                   + ", previousBlockHash=" + Arrays.toString(previousBlockHash)
                   + ", entriesHashList=" + Arrays.toString(entriesHashList)
                   + ", transactionInfo=" + transactionInfo
                   + ", redactionInfo=" + redactionInfo
                   + ", revisions=" + revisions
                   + '}';
       }
   
       @Override
       public boolean equals(final Object o) {
           if (this == o) {
               return true;
           }
           if (!(o instanceof JournalBlock)) {
               return false;
           }
   
           final JournalBlock that = (JournalBlock) o;
   
           if (!getBlockAddress().equals(that.getBlockAddress())) {
               return false;
           }
           if (!getTransactionId().equals(that.getTransactionId())) {
               return false;
           }
           if (!getBlockTimestamp().equals(that.getBlockTimestamp())) {
               return false;
           }
           if (!Arrays.equals(getBlockHash(), that.getBlockHash())) {
               return false;
           }
           if (!Arrays.equals(getEntriesHash(), that.getEntriesHash())) {
               return false;
           }
           if (!Arrays.equals(getPreviousBlockHash(), that.getPreviousBlockHash())) {
               return false;
           }
           if (!Arrays.deepEquals(getEntriesHashList(), that.getEntriesHashList())) {
               return false;
           }
           if (!getTransactionInfo().equals(that.getTransactionInfo())) {
               return false;
           }
           if (getRedactionInfo() != null ? !getRedactionInfo().equals(that.getRedactionInfo()) : that.getRedactionInfo() != null) {
               return false;
           }
           return getRevisions() != null ? getRevisions().equals(that.getRevisions()) : that.getRevisions() == null;
       }
   
       @Override
       public int hashCode() {
           int result = getBlockAddress().hashCode();
           result = 31 * result + getTransactionId().hashCode();
           result = 31 * result + getBlockTimestamp().hashCode();
           result = 31 * result + Arrays.hashCode(getBlockHash());
           result = 31 * result + Arrays.hashCode(getEntriesHash());
           result = 31 * result + Arrays.hashCode(getPreviousBlockHash());
           result = 31 * result + Arrays.deepHashCode(getEntriesHashList());
           result = 31 * result + getTransactionInfo().hashCode();
           result = 31 * result + (getRedactionInfo() != null ? getRedactionInfo().hashCode() : 0);
           result = 31 * result + (getRevisions() != null ? getRevisions().hashCode() : 0);
           return result;
       }
   
       /**
        * This method validates that the hashes of the components of a journal block make up the block
        * hash that is provided with the block itself.
        *
        * The components that contribute to the hash of the journal block consist of the following:
        *   - user transaction information (contained in [transactionInfo])
        *   - user redaction information (contained in [redactionInfo])
        *   - user revisions (contained in [revisions])
        *   - hashes of internal-only system metadata (contained in [revisions] and in [entriesHashList])
        *   - the previous block hash
        *
        * If any of the computed hashes of user information cannot be validated or any of the system
        * hashes do not result in the correct computed values, this method will throw an IllegalArgumentException.
        *
        * Internal-only system metadata is represented by its hash, and can be present in the form of certain
        * items in the [revisions] list that only contain a hash and no user data, as well as some hashes
        * in [entriesHashList].
        *
        * To validate that the hashes of the user data are valid components of the [blockHash], this method
        * performs the following steps:
        *
        * 1. Compute the hash of the [transactionInfo] and validate that it is included in the [entriesHashList].
        * 2. Compute the hash of the [redactionInfo], if present, and validate that it is included in the [entriesHashList].
        * 3. Validate the hash of each user revision was correctly computed and matches the hash published
        * with that revision.
        * 4. Compute the hash of the [revisions] by treating the revision hashes as the leaf nodes of a Merkle tree
        * and calculating the root hash of that tree. Then validate that hash is included in the [entriesHashList].
        * 5. Compute the hash of the [entriesHashList] by treating the hashes as the leaf nodes of a Merkle tree
        * and calculating the root hash of that tree. Then validate that hash matches [entriesHash].
        * 6. Finally, compute the block hash by computing the hash resulting from concatenating the [entriesHash]
        * and previous block hash, and validate that the result matches the [blockHash] provided by QLDB with the block.
        *
        * This method is called by ValidateQldbHashChain::verify for each journal block to validate its
        * contents before verifying that the hash chain between consecutive blocks is correct.
        */
       public void verifyBlockHash() {
           Set<ByteBuffer> entriesHashSet = new HashSet<>();
           Arrays.stream(entriesHashList).forEach(hash -> entriesHashSet.add(wrap(hash).asReadOnlyBuffer()));
   
           byte[] computedTransactionInfoHash = computeTransactionInfoHash();
           if (!entriesHashSet.contains(wrap(computedTransactionInfoHash).asReadOnlyBuffer())) {
               throw new IllegalArgumentException(
                       "Block transactionInfo hash is not contained in the QLDB block entries hash list.");
           }
   
           if (redactionInfo != null) {
               byte[] computedRedactionInfoHash = computeRedactionInfoHash();
               if (!entriesHashSet.contains(wrap(computedRedactionInfoHash).asReadOnlyBuffer())) {
                   throw new IllegalArgumentException(
                           "Block redactionInfo hash is not contained in the QLDB block entries hash list.");
               }
           }
   
           if (revisions != null) {
               revisions.forEach(QldbRevision::verifyRevisionHash);
               byte[] computedRevisionsHash = computeRevisionsHash();
               if (!entriesHashSet.contains(wrap(computedRevisionsHash).asReadOnlyBuffer())) {
                   throw new IllegalArgumentException(
                           "Block revisions list hash is not contained in the QLDB block entries hash list.");
               }
           }
   
           byte[] computedEntriesHash = computeEntriesHash();
           if (!Arrays.equals(computedEntriesHash, entriesHash)) {
               throw new IllegalArgumentException("Computed entries hash does not match entries hash provided in the block.");
           }
   
           byte[] computedBlockHash = Verifier.dot(computedEntriesHash, previousBlockHash);
           if (!Arrays.equals(computedBlockHash, blockHash)) {
               throw new IllegalArgumentException("Computed block hash does not match block hash provided in the block.");
           }
       }
   
       private byte[] computeTransactionInfoHash() {
           try {
               return QldbIonUtils.hashIonValue(Constants.MAPPER.writeValueAsIonValue(transactionInfo));
           } catch (IOException e) {
               throw new IllegalArgumentException("Could not compute transactionInfo hash to verify block hash.", e);
           }
       }
   
       private byte[] computeRedactionInfoHash() {
           try {
               return QldbIonUtils.hashIonValue(Constants.MAPPER.writeValueAsIonValue(redactionInfo));
           } catch (IOException e) {
               throw new IllegalArgumentException("Could not compute redactionInfo hash to verify block hash.", e);
           }
       }
   
       private byte[] computeRevisionsHash() {
           return Verifier.calculateMerkleTreeRootHash(revisions.stream().map(QldbRevision::getHash).collect(Collectors.toList()));
       }
   
       private byte[] computeEntriesHash() {
           return Verifier.calculateMerkleTreeRootHash(Arrays.asList(entriesHashList));
       }
   }
   ```

1. Compila ed esegui il seguente programma (`ValidateQldbHashChain.java`) per eseguire i seguenti passaggi:

   1. Esporta i blocchi del journal dal `vehicle-registration` registro in un bucket Amazon S3 **qldb-tutorial-journal-export-*111122223333*** denominato (sostituiscilo con Account AWS il tuo numero).

   1. Convalida i singoli componenti hash all'interno di ogni blocco chiamando. `verifyBlockHash()`

   1. Convalida la catena hash tra i blocchi di journal.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazonaws.services.qldb.model.ExportJournalToS3Result;
   import com.amazonaws.services.qldb.model.S3EncryptionConfiguration;
   import com.amazonaws.services.qldb.model.S3ObjectEncryptionType;
   import com.amazonaws.services.s3.AmazonS3ClientBuilder;
   
   import java.time.Instant;
   import java.util.Arrays;
   import java.util.List;
   
   import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
   import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import software.amazon.qldb.tutorial.qldb.JournalBlock;
   
   /**
    * Validate the hash chain of a QLDB ledger by stepping through its S3 export.
    *
    * This code accepts an exportId as an argument, if exportId is passed the code
    * will use that or request QLDB to generate a new export to perform QLDB hash
    * chain validation.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class ValidateQldbHashChain {
       public static final Logger log = LoggerFactory.getLogger(ValidateQldbHashChain.class);
       private static final int TIME_SKEW = 20;
   
       private ValidateQldbHashChain() { }
   
       /**
        * Export journal contents to a S3 bucket.
        *
        * @return the ExportId of the journal export.
        * @throws InterruptedException if the thread is interrupted while waiting for export to complete.
        */
       private static String createExport() throws InterruptedException {
           String accountId = AWSSecurityTokenServiceClientBuilder.defaultClient()
               .getCallerIdentity(new GetCallerIdentityRequest()).getAccount();
           String bucketName = Constants.JOURNAL_EXPORT_S3_BUCKET_NAME_PREFIX + "-" + accountId;
           String prefix = Constants.LEDGER_NAME + "-" + Instant.now().getEpochSecond() + "/";
   
           S3EncryptionConfiguration encryptionConfiguration = new S3EncryptionConfiguration()
                   .withObjectEncryptionType(S3ObjectEncryptionType.SSE_S3);
           ExportJournalToS3Result exportJournalToS3Result = 
               ExportJournal.createJournalExportAndAwaitCompletion(Constants.LEDGER_NAME, 
                       bucketName, prefix, null, encryptionConfiguration, ExportJournal.DEFAULT_EXPORT_TIMEOUT_MS);
   
           return exportJournalToS3Result.getExportId();
       }
   
       /**
        * Validates that the chain hash on the {@link JournalBlock} is valid.
        *
        * @param journalBlocks
        *              {@link JournalBlock} containing hashes to validate.
        * @throws IllegalStateException if previous block hash does not match.
        */
       public static void verify(final List<JournalBlock> journalBlocks) {
           if (journalBlocks.size() == 0) {
               return;
           }
   
           journalBlocks.stream().reduce(null, (previousJournalBlock, journalBlock) -> {
               journalBlock.verifyBlockHash();
               if (previousJournalBlock == null) { return journalBlock; }
               if (!Arrays.equals(previousJournalBlock.getBlockHash(), journalBlock.getPreviousBlockHash())) {
                   throw new IllegalStateException("Previous block hash doesn't match.");
               }
               byte[] blockHash = Verifier.dot(journalBlock.getEntriesHash(), previousJournalBlock.getBlockHash());
               if (!Arrays.equals(blockHash, journalBlock.getBlockHash())) {
                   throw new IllegalStateException("Block hash doesn't match entriesHash dot previousBlockHash, the chain is "
                           + "broken.");
               }
               return journalBlock;
           });
       }
   
       public static void main(final String... args) throws InterruptedException {
           try {
               String exportId;
               if (args.length == 1) {
                   exportId = args[0];
                   log.info("Validating QLDB hash chain for exportId: " + exportId);
               } else {
                   log.info("Requesting QLDB to create an export.");
                   exportId = createExport();
               }
               List<JournalBlock> journalBlocks =
                   JournalS3ExportReader.readExport(DescribeJournalExport.describeExport(Constants.LEDGER_NAME,
                       exportId), AmazonS3ClientBuilder.defaultClient());
               verify(journalBlocks);
           } catch (Exception e) {
               log.error("Unable to perform hash chain verification.", e);
               throw e;
           }
       }
   
   }
   ```

Se non hai più bisogno di usare il `vehicle-registration` libro mastro, procedi con. [Passaggio 9 (opzionale): Pulisci le risorse](getting-started.java.step-9.md)

# Passaggio 9 (opzionale): Pulisci le risorse
Fase 9: Pulizia

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Puoi `vehicle-registration` continuare a utilizzare il registro. Tuttavia, se non è più necessario, è necessario eliminarlo.

**Per eliminare il libro mastro**

1. Compila ed esegui il seguente programma (`DeleteLedger.java`) per eliminare il `vehicle-registration` registro e tutto il suo contenuto.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   package software.amazon.qldb.tutorial;
   
   import com.amazonaws.services.qldb.AmazonQLDB;
   import com.amazonaws.services.qldb.model.DeleteLedgerRequest;
   import com.amazonaws.services.qldb.model.DeleteLedgerResult;
   import com.amazonaws.services.qldb.model.ResourceNotFoundException;
   import com.amazonaws.services.qldb.model.UpdateLedgerRequest;
   import com.amazonaws.services.qldb.model.UpdateLedgerResult;
   
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   /**
    * Delete a ledger.
    *
    * This code expects that you have AWS credentials setup per:
    * http://docs.aws.amazon.com/java-sdk/latest/developer-guide/setup-credentials.html
    */
   public final class DeleteLedger {
       public static final Logger log = LoggerFactory.getLogger(DeleteLedger.class);
       public static final Long LEDGER_DELETION_POLL_PERIOD_MS = 20_000L;
       public static AmazonQLDB client = CreateLedger.getClient();
   
       private DeleteLedger() { }
   
       public static void main(String... args) throws Exception {
           try {
               setDeletionProtection(Constants.LEDGER_NAME, false);
   
               delete(Constants.LEDGER_NAME);
   
               waitForDeleted(Constants.LEDGER_NAME);
   
           } catch (Exception e) {
               log.error("Unable to delete the ledger.", e);
               throw e;
           }
       }
   
       /**
        * Send a request to the QLDB database to delete the specified ledger.
        *
        * @param ledgerName
        *              Name of the ledger to be deleted.
        * @return DeleteLedgerResult.
        */
       public static DeleteLedgerResult delete(final String ledgerName) {
           log.info("Attempting to delete the ledger with name: {}...", ledgerName);
           DeleteLedgerRequest request = new DeleteLedgerRequest().withName(ledgerName);
           DeleteLedgerResult result = client.deleteLedger(request);
           log.info("Success.");
           return result;
       }
   
       /**
        * Wait for the ledger to be deleted.
        *
        * @param ledgerName
        *              Name of the ledger being deleted.
        * @throws InterruptedException if thread is being interrupted.
        */
       public static void waitForDeleted(final String ledgerName) throws InterruptedException {
           log.info("Waiting for the ledger to be deleted...");
           while (true) {
               try {
                   DescribeLedger.describe(ledgerName);
                   log.info("The ledger is still being deleted. Please wait...");
                   Thread.sleep(LEDGER_DELETION_POLL_PERIOD_MS);
               } catch (ResourceNotFoundException ex) {
                   log.info("Success. The ledger is deleted.");
                   break;
               }
           }
       }
       
       public static UpdateLedgerResult setDeletionProtection(String ledgerName, boolean deletionProtection) {
           log.info("Let's set deletionProtection to {} for the ledger with name {}", deletionProtection, ledgerName);
           UpdateLedgerRequest request = new UpdateLedgerRequest()
                   .withName(ledgerName)
                   .withDeletionProtection(deletionProtection);
   
           UpdateLedgerResult result = client.updateLedger(request);
           log.info("Success. Ledger updated: {}", result);
           return result;
       }
   }
   ```
**Nota**  
Se la protezione da eliminazione è abilitata per il registro, devi prima disabilitarla prima di poter eliminare il registro utilizzando l'API QLDB.

1. Se hai esportato i dati del journal nel [passaggio precedente](getting-started.java.step-8.md) e non ne hai più bisogno, usa la console Amazon S3 per eliminare il tuo bucket S3.

   Apri la console Amazon S3 all'indirizzo. [https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/)

# Tutorial su Amazon QLDB Node.js
Tutorial su Node.js

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questa implementazione dell'applicazione di esempio del tutorial, utilizzi il driver Amazon QLDB con AWS l'SDK JavaScript per Node.js per creare un registro QLDB e compilarlo con dati di esempio.

[Mentre segui questo tutorial, puoi fare riferimento all'API Reference per le AWS SDK per JavaScript operazioni API di gestione.](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/) Per le operazioni sui dati transazionali, puoi fare riferimento al QLDB Driver for Node.js API [Reference](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/3.1.0/index.html).

**Nota**  
Ove applicabile, alcuni passaggi del tutorial contengono comandi o esempi di codice diversi per ogni versione principale supportata del driver QLDB per Node.js.

**Topics**
+ [

# Installazione dell'applicazione di esempio Amazon QLDB Node.js
](sample-app.nodejs.md)
+ [

# Fase 1: Creare un nuovo libro mastro
](getting-started.nodejs.step-1.md)
+ [

# Passaggio 2: verifica la connettività al registro
](getting-started.nodejs.step-2.md)
+ [

# Passaggio 3: Creare tabelle, indici e dati di esempio
](getting-started.nodejs.step-3.md)
+ [

# Passaggio 4: interrogare le tabelle in un libro mastro
](getting-started.nodejs.step-4.md)
+ [

# Fase 5: Modificare i documenti in un libro mastro
](getting-started.nodejs.step-5.md)
+ [

# Fase 6: Visualizzare la cronologia delle revisioni di un documento
](getting-started.nodejs.step-6.md)
+ [

# Fase 7: Verificare un documento in un libro mastro
](getting-started.nodejs.step-7.md)
+ [

# Passaggio 8 (opzionale): Pulisci le risorse
](getting-started.nodejs.step-8.md)

# Installazione dell'applicazione di esempio Amazon QLDB Node.js
Installazione dell'applicazione di esempio

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questa sezione descrive come installare ed eseguire l'applicazione di esempio Amazon QLDB fornita per step-by-step il tutorial Node.js. Il caso d'uso di questa applicazione di esempio è un database del Dipartimento dei veicoli a motore (DMV) che tiene traccia delle informazioni storiche complete sulle immatricolazioni dei veicoli.

[L'applicazione di esempio DMV per Node.js è open source nel GitHub repository aws-samples/ -nodejs. amazon-qldb-dmv-sample](https://github.com/aws-samples/amazon-qldb-dmv-sample-nodejs)

## Prerequisiti


Prima di iniziare, assicurati di aver completato il driver QLDB per Node.js. [Prerequisiti](getting-started.nodejs.md#getting-started.nodejs.prereqs) Ciò include l'installazione di Node.js e le seguenti operazioni:

1. Registrati per AWS.

1. Crea un utente con le autorizzazioni QLDB appropriate.

1. Concedi l'accesso programmatico per lo sviluppo.

Per completare tutti i passaggi di questo tutorial, è necessario l'accesso amministrativo completo alla risorsa di registro tramite l'API QLDB.

## Installazione


**Per installare l'applicazione di esempio**

1. Immettete il seguente comando da cui clonare l'applicazione di GitHub esempio.

------
#### [ 2.x ]

   ```
   git clone https://github.com/aws-samples/amazon-qldb-dmv-sample-nodejs.git
   ```

------
#### [ 1.x ]

   ```
   git clone -b v1.0.0 https://github.com/aws-samples/amazon-qldb-dmv-sample-nodejs.git
   ```

------

   L'applicazione di esempio racchiude il codice sorgente completo di questo tutorial e le relative dipendenze, inclusi il driver Node.js e l'[AWS SDK per JavaScript Node.js](https://aws.amazon.com/sdk-for-node-js). Questa applicazione è scritta in. TypeScript

1. Passa alla directory in cui viene clonato il `amazon-qldb-dmv-sample-nodejs` pacchetto.

   ```
   cd amazon-qldb-dmv-sample-nodejs
   ```

1. Esegui un'installazione pulita delle dipendenze.

   ```
   npm ci
   ```

1. Transpila il pacchetto.

   ```
   npm run build
   ```

   I JavaScript file transpilati vengono scritti nella directory. `./dist`

1. Procedi con l'[Fase 1: Creare un nuovo libro mastro](getting-started.nodejs.step-1.md)avvio del tutorial e la creazione di un libro mastro.

# Fase 1: Creare un nuovo libro mastro


**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo passaggio, crei un nuovo registro Amazon QLDB denominato. `vehicle-registration`

**Per creare un nuovo libro mastro**

1. Esamina il seguente file (`Constants.ts`), che contiene valori costanti utilizzati da tutti gli altri programmi di questo tutorial.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   /**
    * Constant values used throughout this tutorial.
    */
   export const LEDGER_NAME = "vehicle-registration";
   export const LEDGER_NAME_WITH_TAGS = "tags";
   
   export const DRIVERS_LICENSE_TABLE_NAME = "DriversLicense";
   export const PERSON_TABLE_NAME = "Person";
   export const VEHICLE_REGISTRATION_TABLE_NAME = "VehicleRegistration";
   export const VEHICLE_TABLE_NAME = "Vehicle";
   
   export const GOV_ID_INDEX_NAME = "GovId";
   export const LICENSE_NUMBER_INDEX_NAME = "LicenseNumber";
   export const LICENSE_PLATE_NUMBER_INDEX_NAME = "LicensePlateNumber";
   export const PERSON_ID_INDEX_NAME = "PersonId";
   export const VIN_INDEX_NAME = "VIN";
   
   export const RETRY_LIMIT = 4;
   
   export const JOURNAL_EXPORT_S3_BUCKET_NAME_PREFIX = "qldb-tutorial-journal-export";
   export const USER_TABLES = "information_schema.user_tables";
   ```

1. Utilizzate il seguente programma (`CreateLedger.ts`) per creare un registro denominato`vehicle-registration`.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QLDB } from "aws-sdk";
   import {
       CreateLedgerRequest,
       CreateLedgerResponse,
       DescribeLedgerRequest,
       DescribeLedgerResponse
   } from "aws-sdk/clients/qldb";
   
   import { LEDGER_NAME } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   import { sleep } from "./qldb/Util";
   
   const LEDGER_CREATION_POLL_PERIOD_MS = 10000;
   const ACTIVE_STATE = "ACTIVE";
   
   /**
    * Create a new ledger with the specified name.
    * @param ledgerName Name of the ledger to be created.
    * @param qldbClient The QLDB control plane client to use.
    * @returns Promise which fulfills with a CreateLedgerResponse.
    */
   export async function createLedger(ledgerName: string, qldbClient: QLDB): Promise<CreateLedgerResponse> {
       log(`Creating a ledger named: ${ledgerName}...`);
       const request: CreateLedgerRequest = {
           Name: ledgerName,
           PermissionsMode: "ALLOW_ALL"
       }
       const result: CreateLedgerResponse = await qldbClient.createLedger(request).promise();
       log(`Success. Ledger state: ${result.State}.`);
       return result;
   }
   
   /**
    * Wait for the newly created ledger to become active.
    * @param ledgerName Name of the ledger to be checked on.
    * @param qldbClient The QLDB control plane client to use.
    * @returns Promise which fulfills with a DescribeLedgerResponse.
    */
   export async function waitForActive(ledgerName: string, qldbClient: QLDB): Promise<DescribeLedgerResponse> {
       log(`Waiting for ledger ${ledgerName} to become active...`);
       const request: DescribeLedgerRequest = {
           Name: ledgerName
       }
       while (true) {
           const result: DescribeLedgerResponse = await qldbClient.describeLedger(request).promise();
           if (result.State === ACTIVE_STATE) {
               log("Success. Ledger is active and ready to be used.");
               return result;
           }
           log("The ledger is still creating. Please wait...");
           await sleep(LEDGER_CREATION_POLL_PERIOD_MS);
       }
   }
   
   /**
    * Create a ledger and wait for it to be active.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbClient: QLDB = new QLDB();
           await createLedger(LEDGER_NAME, qldbClient);
           await waitForActive(LEDGER_NAME, qldbClient);
       } catch (e) {
           error(`Unable to create the ledger: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```
**Nota**  
Nella `createLedger` chiamata, è necessario specificare un nome di registro e una modalità di autorizzazione. Ti consigliamo di utilizzare la modalità `STANDARD` autorizzazioni per massimizzare la sicurezza dei dati del registro.
Quando si crea un registro, la *protezione da eliminazione* è abilitata per impostazione predefinita. Questa è una funzionalità di QLDB che impedisce l'eliminazione dei registri da parte di qualsiasi utente. Hai la possibilità di disabilitare la protezione da eliminazione durante la creazione del registro utilizzando l'API QLDB o il (). AWS Command Line Interface AWS CLI
Facoltativamente, puoi anche specificare i tag da allegare al tuo registro.

1. Per eseguire il programma transpiled, immettete il seguente comando.

   ```
   node dist/CreateLedger.js
   ```

Per verificare la connessione al nuovo registro, procedi a. [Passaggio 2: verifica la connettività al registro](getting-started.nodejs.step-2.md)

# Passaggio 2: verifica la connettività al registro
Fase 2: Verificare la connettività

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo passaggio, verifichi di poterti connettere al `vehicle-registration` registro in Amazon QLDB utilizzando l'endpoint API dei dati transazionali.

**Per testare la connettività al registro**

1. Utilizzate il seguente programma (`ConnectToLedger.ts`) per creare una connessione di sessione dati al `vehicle-registration` registro.

------
#### [ 2.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver, RetryConfig  } from "amazon-qldb-driver-nodejs";
   import { ClientConfiguration } from "aws-sdk/clients/qldbsession";
   
   import { LEDGER_NAME } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   
   const qldbDriver: QldbDriver = createQldbDriver();
   
   /**
    * Create a driver for creating sessions.
    * @param ledgerName The name of the ledger to create the driver on.
    * @param serviceConfigurationOptions The configurations for the AWS SDK client that the driver uses.
    * @returns The driver for creating sessions.
    */
   export function createQldbDriver(
       ledgerName: string = LEDGER_NAME,
       serviceConfigurationOptions: ClientConfiguration = {}
   ): QldbDriver {
       const retryLimit = 4;
       const maxConcurrentTransactions = 10;
       //Use driver's default backoff function (and hence, no second parameter provided to RetryConfig)
       const retryConfig: RetryConfig = new RetryConfig(retryLimit);
       const qldbDriver: QldbDriver = new QldbDriver(ledgerName, serviceConfigurationOptions, maxConcurrentTransactions, retryConfig);
       return qldbDriver;
   }
   
   export function getQldbDriver(): QldbDriver {
       return qldbDriver;
   }
   
   /**
    * Connect to a session for a given ledger using default settings.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           log("Listing table names...");
           const tableNames: string[] = await qldbDriver.getTableNames();
           tableNames.forEach((tableName: string): void => {
               log(tableName);
           });
       } catch (e) {
           error(`Unable to create session: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```

------
#### [ 1.x ]

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver  } from "amazon-qldb-driver-nodejs";
   import { ClientConfiguration } from "aws-sdk/clients/qldbsession";
   
   import { LEDGER_NAME } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   
   const qldbDriver: QldbDriver = createQldbDriver();
   
   /**
    * Create a driver for creating sessions.
    * @param ledgerName The name of the ledger to create the driver on.
    * @param serviceConfigurationOptions The configurations for the AWS SDK client that the driver uses.
    * @returns The driver for creating sessions.
    */
   export function createQldbDriver(
       ledgerName: string = LEDGER_NAME,
       serviceConfigurationOptions: ClientConfiguration = {}
   ): QldbDriver {
       const qldbDriver: QldbDriver = new QldbDriver(ledgerName, serviceConfigurationOptions);
       return qldbDriver;
   }
   
   export function getQldbDriver(): QldbDriver {
       return qldbDriver;
   }
   
   /**
    * Connect to a session for a given ledger using default settings.
    * @returns Promise which fulfills with void.
    */
   var main = async function(): Promise<void> {
       try {
           log("Listing table names...");
           const tableNames: string[] = await qldbDriver.getTableNames();
           tableNames.forEach((tableName: string): void => {
               log(tableName);
           });
       } catch (e) {
           error(`Unable to create session: ${e}`);
       } 
   }
   
   if (require.main === module) {
       main();
   }
   ```

------
**Nota**  
Per eseguire transazioni di dati sul registro, è necessario creare un oggetto driver QLDB per connettersi a un registro specifico. Si tratta di un oggetto client diverso dall'`qldbClient`oggetto utilizzato nel [passaggio precedente](getting-started.nodejs.step-1.md) per creare il registro. Quel client precedente viene utilizzato solo per eseguire le operazioni dell'API di gestione elencate in. [Riferimento all'API Amazon QLDB](api-reference.md)

1. Per eseguire il programma transpiled, immettete il seguente comando.

   ```
   node dist/ConnectToLedger.js
   ```

Per creare tabelle nel `vehicle-registration` registro, procedi a. [Passaggio 3: Creare tabelle, indici e dati di esempio](getting-started.nodejs.step-3.md)

# Passaggio 3: Creare tabelle, indici e dati di esempio


**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Quando il tuo registro Amazon QLDB è attivo e accetta connessioni, puoi iniziare a creare tabelle per i dati sui veicoli, i loro proprietari e le loro informazioni di immatricolazione. Dopo aver creato le tabelle e gli indici, puoi caricarli con i dati.

In questo passaggio, crei quattro tabelle nel `vehicle-registration` registro:
+ `VehicleRegistration`
+ `Vehicle`
+ `Person`
+ `DriversLicense`

Vengono inoltre creati i seguenti indici.


****  

| Nome tabella | Campo | 
| --- | --- | 
| VehicleRegistration | VIN | 
| VehicleRegistration | LicensePlateNumber | 
| Vehicle | VIN | 
| Person | GovId | 
| DriversLicense | LicenseNumber | 
| DriversLicense | PersonId | 

Quando si inseriscono dati di esempio, si inseriscono innanzitutto i documenti nella tabella. `Person` Quindi, si utilizzano i dati assegnati `id` dal sistema a ciascun `Person` documento per compilare i campi corrispondenti nei documenti appropriati. `VehicleRegistration` `DriversLicense`

**Suggerimento**  
Come procedura ottimale, utilizzate il sistema assegnato a un documento come chiave esterna`id`. Sebbene sia possibile definire campi destinati a essere identificatori univoci (ad esempio, il VIN di un veicolo), il vero identificatore univoco di un documento è il suo. `id` Questo campo è incluso nei metadati del documento, a cui è possibile eseguire una query nella visualizzazione confermata (*la vista* di una tabella definita dal sistema).  
Per ulteriori informazioni sulle viste in QLDB, vedere. [Concetti principali](ledger-structure.md) Per ulteriori informazioni sui metadati, consulta. [Interrogazione dei metadati dei documenti](working.metadata.md)

**Per creare tabelle e indici**

1. Utilizzate il seguente programma (`CreateTable.ts`) per creare le tabelle menzionate in precedenza.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver, Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   
   import { getQldbDriver } from "./ConnectToLedger";
   import {
       DRIVERS_LICENSE_TABLE_NAME,
       PERSON_TABLE_NAME,
       VEHICLE_REGISTRATION_TABLE_NAME,
       VEHICLE_TABLE_NAME
   } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   
   /**
    * Create multiple tables in a single transaction.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param tableName Name of the table to create.
    * @returns Promise which fulfills with the number of changes to the database.
    */
   export async function createTable(txn: TransactionExecutor, tableName: string): Promise<number> {
       const statement: string = `CREATE TABLE ${tableName}`;
       return await txn.execute(statement).then((result: Result) => {
           log(`Successfully created table ${tableName}.`);
           return result.getResultList().length;
       });
   }
   
   /**
    * Create tables in a QLDB ledger.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbDriver: QldbDriver = getQldbDriver();
           await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
               Promise.all([
                   createTable(txn, VEHICLE_REGISTRATION_TABLE_NAME),
                   createTable(txn, VEHICLE_TABLE_NAME),
                   createTable(txn, PERSON_TABLE_NAME),
                   createTable(txn, DRIVERS_LICENSE_TABLE_NAME)
               ]);
           });
       } catch (e) {
           error(`Unable to create tables: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```
**Nota**  
Questo programma dimostra come utilizzare la `executeLambda` funzione in un'istanza del driver QLDB. In questo esempio, esegui più istruzioni `CREATE TABLE` PartiQL con una singola espressione lambda.  
Questa funzione di esecuzione avvia implicitamente una transazione, esegue tutte le istruzioni in lambda e quindi esegue automaticamente il commit della transazione.

1. Per eseguire il programma transpiled, inserisci il seguente comando.

   ```
   node dist/CreateTable.js
   ```

1. Utilizzate il seguente programma (`CreateIndex.ts`) per creare indici sulle tabelle, come descritto in precedenza.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   
   import { getQldbDriver } from "./ConnectToLedger";
   import {
       DRIVERS_LICENSE_TABLE_NAME,
       GOV_ID_INDEX_NAME,
       LICENSE_NUMBER_INDEX_NAME,
       LICENSE_PLATE_NUMBER_INDEX_NAME,
       PERSON_ID_INDEX_NAME,
       PERSON_TABLE_NAME,
       VEHICLE_REGISTRATION_TABLE_NAME,
       VEHICLE_TABLE_NAME,
       VIN_INDEX_NAME
   } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   
   /**
    * Create an index for a particular table.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param tableName Name of the table to add indexes for.
    * @param indexAttribute Index to create on a single attribute.
    * @returns Promise which fulfills with the number of changes to the database.
    */
   export async function createIndex(
       txn: TransactionExecutor, 
       tableName: string, 
       indexAttribute: string
   ): Promise<number> {
       const statement: string = `CREATE INDEX on ${tableName} (${indexAttribute})`;
       return await txn.execute(statement).then((result) => {
           log(`Successfully created index ${indexAttribute} on table ${tableName}.`);
           return result.getResultList().length;
       });
   }
   
   /**
    * Create indexes on tables in a particular ledger.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbDriver: QldbDriver = getQldbDriver();
           await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
               Promise.all([
                   createIndex(txn, PERSON_TABLE_NAME, GOV_ID_INDEX_NAME),
                   createIndex(txn, VEHICLE_TABLE_NAME, VIN_INDEX_NAME),
                   createIndex(txn, VEHICLE_REGISTRATION_TABLE_NAME, VIN_INDEX_NAME),
                   createIndex(txn, VEHICLE_REGISTRATION_TABLE_NAME, LICENSE_PLATE_NUMBER_INDEX_NAME),
                   createIndex(txn, DRIVERS_LICENSE_TABLE_NAME, PERSON_ID_INDEX_NAME),
                   createIndex(txn, DRIVERS_LICENSE_TABLE_NAME, LICENSE_NUMBER_INDEX_NAME)
               ]);
           });
       } catch (e) {
           error(`Unable to create indexes: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```

1. Per eseguire il programma transpiled, immettete il seguente comando.

   ```
   node dist/CreateIndex.js
   ```

**Per caricare i dati nelle tabelle**

1. Esamina i seguenti `.ts` file.

   1. `SampleData.ts`— Contiene i dati di esempio che vengono inseriti nelle `vehicle-registration` tabelle.

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      import { Decimal } from "ion-js";
      
      const EMPTY_SECONDARY_OWNERS: object[] = [];
      export const DRIVERS_LICENSE = [
          {
              PersonId: "",
              LicenseNumber: "LEWISR261LL",
              LicenseType: "Learner",
              ValidFromDate: new Date("2016-12-20"),
              ValidToDate: new Date("2020-11-15")
          },
          {
              PersonId: "",
              LicenseNumber : "LOGANB486CG",
              LicenseType: "Probationary",
              ValidFromDate : new Date("2016-04-06"),
              ValidToDate : new Date("2020-11-15")
          },
          {
              PersonId: "",
              LicenseNumber : "744 849 301",
              LicenseType: "Full",
              ValidFromDate : new Date("2017-12-06"),
              ValidToDate : new Date("2022-10-15")
          },
          {
              PersonId: "",
              LicenseNumber : "P626-168-229-765",
              LicenseType: "Learner",
              ValidFromDate : new Date("2017-08-16"),
              ValidToDate : new Date("2021-11-15")
          },
          {
              PersonId: "",
              LicenseNumber : "S152-780-97-415-0",
              LicenseType: "Probationary",
              ValidFromDate : new Date("2015-08-15"),
              ValidToDate : new Date("2021-08-21")
          }
      ];
      export const PERSON = [
          {
              FirstName : "Raul",
              LastName : "Lewis",
              DOB : new Date("1963-08-19"),
              Address : "1719 University Street, Seattle, WA, 98109",
              GovId : "LEWISR261LL",
              GovIdType : "Driver License"
          },
          {
              FirstName : "Brent",
              LastName : "Logan",
              DOB : new Date("1967-07-03"),
              Address : "43 Stockert Hollow Road, Everett, WA, 98203",
              GovId : "LOGANB486CG",
              GovIdType : "Driver License"
          },
          {
              FirstName : "Alexis",
              LastName : "Pena",
              DOB : new Date("1974-02-10"),
              Address : "4058 Melrose Street, Spokane Valley, WA, 99206",
              GovId : "744 849 301",
              GovIdType : "SSN"
          },
          {
              FirstName : "Melvin",
              LastName : "Parker",
              DOB : new Date("1976-05-22"),
              Address : "4362 Ryder Avenue, Seattle, WA, 98101",
              GovId : "P626-168-229-765",
              GovIdType : "Passport"
          },
          {
              FirstName : "Salvatore",
              LastName : "Spencer",
              DOB : new Date("1997-11-15"),
              Address : "4450 Honeysuckle Lane, Seattle, WA, 98101",
              GovId : "S152-780-97-415-0",
              GovIdType : "Passport"
          }
      ];
      export const VEHICLE = [
          {
              VIN : "1N4AL11D75C109151",
              Type : "Sedan",
              Year : 2011,
              Make : "Audi",
              Model : "A5",
              Color : "Silver"
          },
          {
              VIN : "KM8SRDHF6EU074761",
              Type : "Sedan",
              Year : 2015,
              Make : "Tesla",
              Model : "Model S",
              Color : "Blue"
          },
          {
              VIN : "3HGGK5G53FM761765",
              Type : "Motorcycle",
              Year : 2011,
              Make : "Ducati",
              Model : "Monster 1200",
              Color : "Yellow"
          },
          {
              VIN : "1HVBBAANXWH544237",
              Type : "Semi",
              Year : 2009,
              Make : "Ford",
              Model : "F 150",
              Color : "Black"
          },
          {
              VIN : "1C4RJFAG0FC625797",
              Type : "Sedan",
              Year : 2019,
              Make : "Mercedes",
              Model : "CLK 350",
              Color : "White"
          }
      ];
      export const VEHICLE_REGISTRATION = [
          {
              VIN : "1N4AL11D75C109151",
              LicensePlateNumber : "LEWISR261LL",
              State : "WA",
              City : "Seattle",
              ValidFromDate : new Date("2017-08-21"),
              ValidToDate : new Date("2020-05-11"),
              PendingPenaltyTicketAmount : new Decimal(9025, -2),
              Owners : {
                  PrimaryOwner : { PersonId : "" },
                  SecondaryOwners : EMPTY_SECONDARY_OWNERS
              }
          },
          {
              VIN : "KM8SRDHF6EU074761",
              LicensePlateNumber : "CA762X",
              State : "WA",
              City : "Kent",
              PendingPenaltyTicketAmount : new Decimal(13075, -2),
              ValidFromDate : new Date("2017-09-14"),
              ValidToDate : new Date("2020-06-25"),
              Owners : {
                  PrimaryOwner : { PersonId : "" },
                  SecondaryOwners : EMPTY_SECONDARY_OWNERS
              }
          },
          {
              VIN : "3HGGK5G53FM761765",
              LicensePlateNumber : "CD820Z",
              State : "WA",
              City : "Everett",
              PendingPenaltyTicketAmount : new Decimal(44230, -2),
              ValidFromDate : new Date("2011-03-17"),
              ValidToDate : new Date("2021-03-24"),
              Owners : {
                  PrimaryOwner : { PersonId : "" },
                  SecondaryOwners : EMPTY_SECONDARY_OWNERS
              }
          },
          {
              VIN : "1HVBBAANXWH544237",
              LicensePlateNumber : "LS477D",
              State : "WA",
              City : "Tacoma",
              PendingPenaltyTicketAmount : new Decimal(4220, -2),
              ValidFromDate : new Date("2011-10-26"),
              ValidToDate : new Date("2023-09-25"),
              Owners : {
                  PrimaryOwner : { PersonId : "" },
                  SecondaryOwners : EMPTY_SECONDARY_OWNERS
              }
          },
          {
              VIN : "1C4RJFAG0FC625797",
              LicensePlateNumber : "TH393F",
              State : "WA",
              City : "Olympia",
              PendingPenaltyTicketAmount : new Decimal(3045, -2),
              ValidFromDate : new Date("2013-09-02"),
              ValidToDate : new Date("2024-03-19"),
              Owners : {
                  PrimaryOwner : { PersonId : "" },
                  SecondaryOwners : EMPTY_SECONDARY_OWNERS
              }
          }
      ];
      ```

   1. `Util.ts`— Un modulo di utilità che importa dal `ion-js` pacchetto per fornire funzioni di supporto che convertono, analizzano e stampano i dati di [Amazon Ion.](ion.md)

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      import { Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";
      import { GetBlockResponse, GetDigestResponse, ValueHolder } from "aws-sdk/clients/qldb";
      import { 
          Decimal, 
          decodeUtf8,
          dom,
          IonTypes, 
          makePrettyWriter, 
          makeReader, 
          Reader, 
          Timestamp, 
          toBase64, 
          Writer
      } from "ion-js";
      
      import { error } from "./LogUtil";
      
      
      
      /**
       * TODO: Replace this with json.stringify
       * Returns the string representation of a given BlockResponse.
       * @param blockResponse The BlockResponse to convert to string.
       * @returns The string representation of the supplied BlockResponse.
       */
      export function blockResponseToString(blockResponse: GetBlockResponse): string {
          let stringBuilder: string = "";
          if (blockResponse.Block.IonText) {
              stringBuilder = stringBuilder + "Block: " + blockResponse.Block.IonText + ", ";
          }
          if (blockResponse.Proof.IonText) {
              stringBuilder = stringBuilder + "Proof: " + blockResponse.Proof.IonText;
          }
          stringBuilder = "{" + stringBuilder + "}";
          const writer: Writer = makePrettyWriter();
          const reader: Reader = makeReader(stringBuilder);
          writer.writeValues(reader);
          return decodeUtf8(writer.getBytes());
      }
      
      /**
       * TODO: Replace this with json.stringify
       * Returns the string representation of a given GetDigestResponse.
       * @param digestResponse The GetDigestResponse to convert to string.
       * @returns The string representation of the supplied GetDigestResponse.
       */
      export function digestResponseToString(digestResponse: GetDigestResponse): string {
          let stringBuilder: string = "";
          if (digestResponse.Digest) {
              stringBuilder += "Digest: " + JSON.stringify(toBase64(<Uint8Array> digestResponse.Digest)) + ", ";
          }
          if (digestResponse.DigestTipAddress.IonText) {
              stringBuilder += "DigestTipAddress: " + digestResponse.DigestTipAddress.IonText;
          }
          stringBuilder = "{" + stringBuilder + "}";
          const writer: Writer = makePrettyWriter();
          const reader: Reader = makeReader(stringBuilder);
          writer.writeValues(reader);
          return decodeUtf8(writer.getBytes());
      }
      
      /**
       * Get the document IDs from the given table.
       * @param txn The {@linkcode TransactionExecutor} for lambda execute.
       * @param tableName The table name to query.
       * @param field A field to query.
       * @param value The key of the given field.
       * @returns Promise which fulfills with the document ID as a string.
       */
      export async function getDocumentId(
          txn: TransactionExecutor,
          tableName: string,
          field: string,
          value: string
      ): Promise<string> {
          const query: string = `SELECT id FROM ${tableName} AS t BY id WHERE t.${field} = ?`;
          let documentId: string = undefined;
          await txn.execute(query, value).then((result: Result) => {
              const resultList: dom.Value[] = result.getResultList();
              if (resultList.length === 0) {
                  throw new Error(`Unable to retrieve document ID using ${value}.`);
              }
              documentId = resultList[0].get("id").stringValue();
      
          }).catch((err: any) => {
              error(`Error getting documentId: ${err}`);
          });
          return documentId;
      }
      
      /**
       * Sleep for the specified amount of time.
       * @param ms The amount of time to sleep in milliseconds.
       * @returns Promise which fulfills with void.
       */
      export function sleep(ms: number): Promise<void> {
          return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      /**
       * Find the value of a given path in an Ion value. The path should contain a blob value.
       * @param value The Ion value that contains the journal block attributes.
       * @param path The path to a certain attribute.
       * @returns Uint8Array value of the blob, or null if the attribute cannot be found in the Ion value
       *                  or is not of type Blob
       */
      export function getBlobValue(value: dom.Value, path: string): Uint8Array | null {
          const attribute: dom.Value = value.get(path);
          if (attribute !== null && attribute.getType() === IonTypes.BLOB) {
              return attribute.uInt8ArrayValue();
          }
          return null;
      }
      
      /**
       * TODO: Replace this with json.stringify
       * Returns the string representation of a given ValueHolder.
       * @param valueHolder The ValueHolder to convert to string.
       * @returns The string representation of the supplied ValueHolder.
       */
      export function valueHolderToString(valueHolder: ValueHolder): string {
          const stringBuilder: string = `{ IonText: ${valueHolder.IonText}}`;
          const writer: Writer = makePrettyWriter();
          const reader: Reader = makeReader(stringBuilder);
          writer.writeValues(reader);
          return decodeUtf8(writer.getBytes());
      }
      
      /**
       * Converts a given value to Ion using the provided writer.
       * @param value The value to convert to Ion.
       * @param ionWriter The Writer to pass the value into.
       * @throws Error: If the given value cannot be converted to Ion.
       */
      export function writeValueAsIon(value: any, ionWriter: Writer): void {
          switch (typeof value) {
              case "string":
                  ionWriter.writeString(value);
                  break;
              case "boolean":
                  ionWriter.writeBoolean(value);
                  break;
              case "number":
                      ionWriter.writeInt(value);
                      break;
              case "object":
                  if (Array.isArray(value)) {
                      // Object is an array.
                      ionWriter.stepIn(IonTypes.LIST);
      
                      for (const element of value) {
                          writeValueAsIon(element, ionWriter);
                      }
      
                      ionWriter.stepOut();
                  } else if (value instanceof Date) {
                      // Object is a Date.
                      ionWriter.writeTimestamp(Timestamp.parse(value.toISOString()));
                  } else if (value instanceof Decimal) {
                      // Object is a Decimal.
                      ionWriter.writeDecimal(value);
                  } else if (value === null) {
                      ionWriter.writeNull(IonTypes.NULL);
                  } else {
                      // Object is a struct.
                      ionWriter.stepIn(IonTypes.STRUCT);
      
                      for (const key of Object.keys(value)) {
                          ionWriter.writeFieldName(key);
                          writeValueAsIon(value[key], ionWriter);
                      }
                      ionWriter.stepOut();
                  }
                  break;
              default:
                  throw new Error(`Cannot convert to Ion for type: ${(typeof value)}.`);
          }
      }
      ```
**Nota**  
La `getDocumentId` funzione esegue una query che restituisce un documento IDs assegnato dal sistema da una tabella. Per ulteriori informazioni, consulta [Utilizzo della clausola BY per interrogare l'ID del documento](working.metadata.by-clause.md).

1. Utilizzate il seguente programma (`InsertDocument.ts`) per inserire i dati di esempio nelle tabelle.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver, Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   import { dom } from "ion-js";
   
   import { getQldbDriver } from "./ConnectToLedger";
   import { DRIVERS_LICENSE, PERSON, VEHICLE, VEHICLE_REGISTRATION } from "./model/SampleData";
   import {
       DRIVERS_LICENSE_TABLE_NAME,
       PERSON_TABLE_NAME,
       VEHICLE_REGISTRATION_TABLE_NAME,
       VEHICLE_TABLE_NAME
   } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   
   /**
    * Insert the given list of documents into a table in a single transaction.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param tableName Name of the table to insert documents into.
    * @param documents List of documents to insert.
    * @returns Promise which fulfills with a {@linkcode Result} object.
    */
   export async function insertDocument(
       txn: TransactionExecutor,
       tableName: string,
       documents: object[]
   ): Promise<Result> {
       const statement: string = `INSERT INTO ${tableName} ?`;
       const result: Result = await txn.execute(statement, documents);
       return result;
   }
   
   /**
    * Handle the insertion of documents and updating PersonIds all in a single transaction.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @returns Promise which fulfills with void.
    */
   async function updateAndInsertDocuments(txn: TransactionExecutor): Promise<void> {
       log("Inserting multiple documents into the 'Person' table...");
       const documentIds: Result = await insertDocument(txn, PERSON_TABLE_NAME, PERSON);
   
       const listOfDocumentIds: dom.Value[] = documentIds.getResultList();
       log("Updating PersonIds for 'DriversLicense' and PrimaryOwner for 'VehicleRegistration'...");
       updatePersonId(listOfDocumentIds);
   
       log("Inserting multiple documents into the remaining tables...");
       await Promise.all([
           insertDocument(txn, DRIVERS_LICENSE_TABLE_NAME, DRIVERS_LICENSE),
           insertDocument(txn, VEHICLE_REGISTRATION_TABLE_NAME, VEHICLE_REGISTRATION),
           insertDocument(txn, VEHICLE_TABLE_NAME, VEHICLE)
       ]);
   }
   
   /**
    * Update the PersonId value for DriversLicense records and the PrimaryOwner value for VehicleRegistration records.
    * @param documentIds List of document IDs.
    */
   export function updatePersonId(documentIds: dom.Value[]): void {
       documentIds.forEach((value: dom.Value, i: number) => {
           const documentId: string = value.get("documentId").stringValue();
           DRIVERS_LICENSE[i].PersonId = documentId;
           VEHICLE_REGISTRATION[i].Owners.PrimaryOwner.PersonId = documentId;
       });
   }
   
   /**
    * Insert documents into a table in a QLDB ledger.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbDriver: QldbDriver = getQldbDriver();
           await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
               await updateAndInsertDocuments(txn);
           });
       } catch (e) {
           error(`Unable to insert documents: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```
**Nota**  
Questo programma dimostra come chiamare la `execute` funzione con valori parametrizzati. È possibile passare parametri di dati oltre all'istruzione PartiQL che si desidera eseguire. Utilizzate un punto interrogativo (`?`) come segnaposto variabile nella stringa dell'istruzione.
Se un'`INSERT`istruzione ha esito positivo, restituisce il valore `id` di ogni documento inserito.

1. Per eseguire il programma transpiled, immettete il seguente comando.

   ```
   node dist/InsertDocument.js
   ```

Successivamente, è possibile utilizzare `SELECT` le istruzioni per leggere i dati dalle tabelle del registro. `vehicle-registration` Passa a [Passaggio 4: interrogare le tabelle in un libro mastro](getting-started.nodejs.step-4.md).

# Passaggio 4: interrogare le tabelle in un libro mastro
Fase 4: Interrogare le tabelle

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Dopo aver creato tabelle in un registro Amazon QLDB e averle caricate con i dati, puoi eseguire query per rivedere i dati di immatricolazione dei veicoli che hai appena inserito. QLDB [utilizza](ql-reference.md) PartiQL come linguaggio di interrogazione [e Amazon](ion.md) Ion come modello di dati orientato ai documenti.

PartiQL è un linguaggio di query open source compatibile con SQL che è stato esteso per funzionare con Ion. Con PartiQL, puoi inserire, interrogare e gestire i tuoi dati con operatori SQL familiari. Amazon Ion è un superset di JSON. Ion è un formato di dati open source basato su documenti che offre la flessibilità di archiviazione ed elaborazione di dati strutturati, semistrutturati e annidati.

In questo passaggio, si utilizzano `SELECT` le istruzioni per leggere i dati dalle tabelle del registro. `vehicle-registration`

**avvertimento**  
Quando si esegue una query in QLDB senza una ricerca indicizzata, viene richiamata una scansione completa della tabella. PartiQL supporta tali query perché è compatibile con SQL. Tuttavia, *non* eseguire scansioni di tabelle per casi d'uso di produzione in QLDB. Le scansioni delle tabelle possono causare problemi di prestazioni su tabelle di grandi dimensioni, inclusi conflitti di concorrenza e timeout delle transazioni.  
Per evitare le scansioni delle tabelle, è necessario 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)` Per ulteriori informazioni, consulta [Ottimizzazione delle prestazioni delle query](working.optimize.md).

**Per interrogare le tabelle**

1. Usa il seguente programma (`FindVehicles.ts`) per interrogare tutti i veicoli registrati con una persona nel tuo registro.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver, Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   import { dom } from "ion-js";
   
   import { getQldbDriver } from "./ConnectToLedger";
   import { PERSON } from "./model/SampleData";
   import { PERSON_TABLE_NAME } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   import { getDocumentId } from "./qldb/Util";
   import { prettyPrintResultList } from "./ScanTable";
   
   /**
    * Query 'Vehicle' and 'VehicleRegistration' tables using a unique document ID in one transaction.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param govId The owner's government ID.
    * @returns Promise which fulfills with void.
    */
   async function findVehiclesForOwner(txn: TransactionExecutor, govId: string): Promise<void> {
       const documentId: string = await getDocumentId(txn, PERSON_TABLE_NAME, "GovId", govId);
       const query: string = "SELECT Vehicle FROM Vehicle INNER JOIN VehicleRegistration AS r " +
                           "ON Vehicle.VIN = r.VIN WHERE r.Owners.PrimaryOwner.PersonId = ?";
   
       await txn.execute(query, documentId).then((result: Result) => {
           const resultList: dom.Value[] = result.getResultList();
           log(`List of vehicles for owner with GovId: ${govId}`);
           prettyPrintResultList(resultList);
       });
   }
   
   /**
    * Find all vehicles registered under a person.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbDriver: QldbDriver = getQldbDriver();
           await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
               await findVehiclesForOwner(txn, PERSON[0].GovId);
           });
       } catch (e) {
           error(`Error getting vehicles for owner: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```
**Nota**  
Innanzitutto, questo programma interroga la `Person` tabella del documento per ottenere il relativo `GovId LEWISR261LL` campo di `id` metadati.  
Quindi, utilizza questo documento `id` come chiave esterna per interrogare la `VehicleRegistration` tabella. `PrimaryOwner.PersonId` Inoltre si unisce `VehicleRegistration` alla `Vehicle` tabella sul `VIN` campo.

1. Per eseguire il programma transpiled, inserisci il seguente comando.

   ```
   node dist/FindVehicles.js
   ```

Per ulteriori informazioni sulla modifica dei documenti nelle tabelle del `vehicle-registration` libro mastro, vedere. [Fase 5: Modificare i documenti in un libro mastro](getting-started.nodejs.step-5.md)

# Fase 5: Modificare i documenti in un libro mastro
Fase 5: Modificare i documenti

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Ora che hai dei dati su cui lavorare, puoi iniziare a modificare i documenti nel `vehicle-registration` registro di Amazon QLDB. In questa fase, i seguenti esempi di codice mostrano come eseguire istruzioni DML (Data Manipulation Language). Queste istruzioni aggiornano il proprietario principale di un veicolo e aggiungono un proprietario secondario a un altro veicolo.

**Per modificare i documenti**

1. Utilizzate il seguente programma (`TransferVehicleOwnership.ts`) per aggiornare il proprietario principale del veicolo inserendo il `1N4AL11D75C109151` VIN nel registro.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver, Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   import { dom } from "ion-js";
   
   import { getQldbDriver } from "./ConnectToLedger";
   import { PERSON, VEHICLE } from "./model/SampleData";
   import { PERSON_TABLE_NAME } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   import { getDocumentId } from "./qldb/Util";
   
   /**
    * Query a driver's information using the given ID.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param documentId The unique ID of a document in the Person table.
    * @returns Promise which fulfills with an Ion value containing the person.
    */
   export async function findPersonFromDocumentId(txn: TransactionExecutor, documentId: string): Promise<dom.Value> {
       const query: string = "SELECT p.* FROM Person AS p BY pid WHERE pid = ?";
   
       let personId: dom.Value;
       await txn.execute(query, documentId).then((result: Result) => {
           const resultList: dom.Value[] = result.getResultList();
           if (resultList.length === 0) {
               throw new Error(`Unable to find person with ID: ${documentId}.`);
           }
           personId = resultList[0];
       });
       return personId;
   }
   
   /**
    * Find the primary owner for the given VIN.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param vin The VIN to find primary owner for.
    * @returns Promise which fulfills with an Ion value containing the primary owner.
    */
   export async function findPrimaryOwnerForVehicle(txn: TransactionExecutor, vin: string): Promise<dom.Value> {
       log(`Finding primary owner for vehicle with VIN: ${vin}`);
       const query: string = "SELECT Owners.PrimaryOwner.PersonId FROM VehicleRegistration AS v WHERE v.VIN = ?";
   
       let documentId: string = undefined;
       await txn.execute(query, vin).then((result: Result) => {
           const resultList: dom.Value[] = result.getResultList();
           if (resultList.length === 0) {
               throw new Error(`Unable to retrieve document ID using ${vin}.`);
           }
           const PersonIdValue: dom.Value = resultList[0].get("PersonId");
           if (PersonIdValue === null) {
               throw new Error(`Expected field name PersonId not found.`);
           }
           documentId = PersonIdValue.stringValue();
       });
       return findPersonFromDocumentId(txn, documentId);
   }
   
   /**
    * Update the primary owner for a vehicle using the given VIN.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param vin The VIN for the vehicle to operate on.
    * @param documentId New PersonId for the primary owner.
    * @returns Promise which fulfills with void.
    */
   async function updateVehicleRegistration(txn: TransactionExecutor, vin: string, documentId: string): Promise<void> {
       const statement: string = "UPDATE VehicleRegistration AS r SET r.Owners.PrimaryOwner.PersonId = ? WHERE r.VIN = ?";
   
       log(`Updating the primary owner for vehicle with VIN: ${vin}...`);
       await txn.execute(statement, documentId, vin).then((result: Result) => {
           const resultList: dom.Value[] = result.getResultList();
           if (resultList.length === 0) {
               throw new Error("Unable to transfer vehicle, could not find registration.");
           }
           log(`Successfully transferred vehicle with VIN ${vin} to new owner.`);
       });
   }
   
   /**
    * Validate the current owner of the given vehicle and transfer its ownership to a new owner in a single transaction.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param vin The VIN of the vehicle to transfer ownership of.
    * @param currentOwner The GovId of the current owner of the vehicle.
    * @param newOwner The GovId of the new owner of the vehicle.
    */
   export async function validateAndUpdateRegistration(
       txn: TransactionExecutor,
       vin: string,
       currentOwner: string,
       newOwner: string
   ): Promise<void> {
       const primaryOwner: dom.Value = await findPrimaryOwnerForVehicle(txn, vin);
       const govIdValue: dom.Value = primaryOwner.get("GovId");
       if (govIdValue !== null && govIdValue.stringValue() !== currentOwner) {
           log("Incorrect primary owner identified for vehicle, unable to transfer.");
       }
       else {
           const documentId: string = await getDocumentId(txn, PERSON_TABLE_NAME, "GovId", newOwner);
           await updateVehicleRegistration(txn, vin, documentId);
           log("Successfully transferred vehicle ownership!");
       }
   }
   
   /**
    * Find primary owner for a particular vehicle's VIN.
    * Transfer to another primary owner for a particular vehicle's VIN.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbDriver: QldbDriver = getQldbDriver();
   
           const vin: string = VEHICLE[0].VIN;
           const previousOwnerGovId: string = PERSON[0].GovId;
           const newPrimaryOwnerGovId: string = PERSON[1].GovId;
   
           await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
               await validateAndUpdateRegistration(txn, vin, previousOwnerGovId,  newPrimaryOwnerGovId);
           });
       } catch (e) {
           error(`Unable to connect and run queries: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```

1. Per eseguire il programma transpiled, immettete il seguente comando.

   ```
   node dist/TransferVehicleOwnership.js
   ```

1. Utilizzate il seguente programma (`AddSecondaryOwner.ts`) per aggiungere un proprietario secondario al veicolo con VIN `KM8SRDHF6EU074761` nel registro.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver, Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   import { dom } from "ion-js";
   
   import { getQldbDriver } from "./ConnectToLedger";
   import { PERSON, VEHICLE_REGISTRATION } from "./model/SampleData";
   import { PERSON_TABLE_NAME } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   import { getDocumentId } from "./qldb/Util";
   import { prettyPrintResultList } from "./ScanTable";
   
   /**
    * Add a secondary owner into 'VehicleRegistration' table for a particular VIN.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param vin VIN of the vehicle to query.
    * @param secondaryOwnerId The secondary owner's person ID.
    * @returns Promise which fulfills with void.
    */
   export async function addSecondaryOwner(
       txn: TransactionExecutor, 
       vin: string, 
       secondaryOwnerId: string
   ): Promise<void> {
       log(`Inserting secondary owner for vehicle with VIN: ${vin}`);
       const query: string =
           `FROM VehicleRegistration AS v WHERE v.VIN = ? INSERT INTO v.Owners.SecondaryOwners VALUE ?`;
   
       const personToInsert = {PersonId: secondaryOwnerId};
       await txn.execute(query, vin, personToInsert).then(async (result: Result) => {
           const resultList: dom.Value[] = result.getResultList();
           log("VehicleRegistration Document IDs which had secondary owners added: ");
           prettyPrintResultList(resultList);
       });
   }
   
   /**
    * Query for a document ID with a government ID.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param governmentId The government ID to query with.
    * @returns Promise which fulfills with the document ID as a string.
    */
   export async function getDocumentIdByGovId(txn: TransactionExecutor, governmentId: string): Promise<string> {
       const documentId: string = await getDocumentId(txn, PERSON_TABLE_NAME, "GovId", governmentId);
       return documentId;
   }
   
   /**
    * Check whether a driver has already been registered for the given VIN.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param vin VIN of the vehicle to query.
    * @param secondaryOwnerId The secondary owner's person ID.
    * @returns Promise which fulfills with a boolean.
    */
   export async function isSecondaryOwnerForVehicle(
       txn: TransactionExecutor,
       vin: string,
       secondaryOwnerId: string
   ): Promise<boolean> {
       log(`Finding secondary owners for vehicle with VIN: ${vin}`);
       const query: string = "SELECT Owners.SecondaryOwners FROM VehicleRegistration AS v WHERE v.VIN = ?";
   
       let doesExist: boolean = false;
   
       await txn.execute(query, vin).then((result: Result) => {
           const resultList: dom.Value[] = result.getResultList();
   
           resultList.forEach((value: dom.Value) => {
               const secondaryOwnersList: dom.Value[] = value.get("SecondaryOwners").elements();
   
               secondaryOwnersList.forEach((secondaryOwner) => {
                   const personId: dom.Value = secondaryOwner.get("PersonId");
                   if (personId !== null &&  personId.stringValue() === secondaryOwnerId) {
                       doesExist = true;
                   }
               });
           });
       });
       return doesExist;
   }
   
   /**
    * Finds and adds secondary owners for a vehicle.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbDriver: QldbDriver = getQldbDriver();
           const vin: string = VEHICLE_REGISTRATION[1].VIN;
           const govId: string = PERSON[0].GovId;
   
           await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
               const documentId: string = await getDocumentIdByGovId(txn, govId);
   
               if (await isSecondaryOwnerForVehicle(txn, vin, documentId)) {
                   log(`Person with ID ${documentId} has already been added as a secondary owner of this vehicle.`);
               } else {
                   await addSecondaryOwner(txn, vin, documentId);
               }
           });
   
           log("Secondary owners successfully updated.");
       } catch (e) {
           error(`Unable to add secondary owner: ${e}`);
       } 
   }
   
   if (require.main === module) {
       main();
   }
   ```

1. Per eseguire il programma transpiled, immettete il seguente comando.

   ```
   node dist/AddSecondaryOwner.js
   ```

Per esaminare queste modifiche nel `vehicle-registration` registro, vedere. [Fase 6: Visualizzare la cronologia delle revisioni di un documento](getting-started.nodejs.step-6.md)

# Fase 6: Visualizzare la cronologia delle revisioni di un documento
Fase 6: Visualizza la cronologia delle revisioni

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Dopo aver modificato i dati di immatricolazione di un veicolo nel [passaggio precedente](getting-started.nodejs.step-5.md), puoi interrogare la cronologia di tutti i proprietari registrati e qualsiasi altro campo aggiornato. In questo passaggio, esegui una query sulla cronologia delle revisioni di un documento nella `VehicleRegistration` tabella del registro. `vehicle-registration`

**Per visualizzare la cronologia delle revisioni**

1. Utilizzate il seguente programma (`QueryHistory.ts`) per interrogare la cronologia delle revisioni del `VehicleRegistration` documento con VIN. `1N4AL11D75C109151`

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver, Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   import { dom } from "ion-js";
   
   import { getQldbDriver } from "./ConnectToLedger";
   import { VEHICLE_REGISTRATION } from "./model/SampleData";
   import { VEHICLE_REGISTRATION_TABLE_NAME } from "./qldb/Constants";
   import { prettyPrintResultList } from "./ScanTable";
   import { error, log } from "./qldb/LogUtil";
   import { getDocumentId } from "./qldb/Util";
   
   /**
    * Find previous primary owners for the given VIN in a single transaction.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param vin The VIN to find previous primary owners for.
    * @returns Promise which fulfills with void.
    */
   async function previousPrimaryOwners(txn: TransactionExecutor, vin: string): Promise<void> {
       const documentId: string = await getDocumentId(txn, VEHICLE_REGISTRATION_TABLE_NAME, "VIN", vin);
       const todaysDate: Date = new Date();
       // set todaysDate back one minute to ensure end time is in the past
       // by the time the request reaches our backend
       todaysDate.setMinutes(todaysDate.getMinutes() - 1);
       const threeMonthsAgo: Date = new Date(todaysDate);
       threeMonthsAgo.setMonth(todaysDate.getMonth() - 3);
   
       const query: string =
           `SELECT data.Owners.PrimaryOwner, metadata.version FROM history ` +
           `(${VEHICLE_REGISTRATION_TABLE_NAME}, \`${threeMonthsAgo.toISOString()}\`, \`${todaysDate.toISOString()}\`) ` +
           `AS h WHERE h.metadata.id = ?`;
   
       await txn.execute(query, documentId).then((result: Result) => {
           log(`Querying the 'VehicleRegistration' table's history using VIN: ${vin}.`);
           const resultList: dom.Value[] = result.getResultList();
           prettyPrintResultList(resultList);
       });
   }
   
   /**
    * Query a table's history for a particular set of documents.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbDriver: QldbDriver = getQldbDriver();
           const vin: string = VEHICLE_REGISTRATION[0].VIN;
           await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
               await previousPrimaryOwners(txn, vin);
           });
       } catch (e) {
           error(`Unable to query history to find previous owners: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```
**Nota**  
È possibile visualizzare la cronologia delle revisioni di un documento interrogando la sintassi incorporata [Funzione di cronologia](working.history.md#working.history.function) nella seguente sintassi.  

     ```
     SELECT * FROM history( table_name [, `start-time` [, `end-time` ] ] ) AS h
     [ WHERE h.metadata.id = 'id' ]
     ```
L'*ora di inizio* e l'ora di *fine* sono entrambe opzionali. Sono valori letterali di Amazon Ion che possono essere indicati con backticks (). ``...`` Per ulteriori informazioni, consulta [Interrogazione di Ion con PartiQL in Amazon QLDB](ql-reference.query.md).
Come best practice, qualifica una query cronologica con un intervallo di date (ora di *inizio e ora di fine) e un* *ID del documento ()*. `metadata.id` [QLDB `SELECT` elabora le query nelle transazioni, che sono soggette a un limite di timeout delle transazioni.](limits.md#limits.fixed)  
La cronologia QLDB è indicizzata in base all'ID del documento e al momento non è possibile creare indici di cronologia aggiuntivi. Le interrogazioni cronologiche che includono l'ora di inizio e l'ora di fine ottengono il vantaggio della qualificazione per intervalli di date.

1. Per eseguire il programma transpiled, immettete il seguente comando.

   ```
   node dist/QueryHistory.js
   ```

Per verificare crittograficamente una revisione del documento nel registro, procedi a. `vehicle-registration` [Fase 7: Verificare un documento in un libro mastro](getting-started.nodejs.step-7.md)

# Fase 7: Verificare un documento in un libro mastro
Fase 7: Verifica di un documento

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Con Amazon QLDB, puoi verificare in modo efficiente l'integrità di un documento nel diario del tuo libro mastro utilizzando l'hashing crittografico con SHA-256. Per ulteriori informazioni su come funzionano la verifica e l'hashing crittografico in QLDB, consulta. [Verifica dei dati in Amazon QLDB](verification.md)

In questo passaggio, verifichi una revisione del documento nella tabella del `VehicleRegistration` registro. `vehicle-registration` Innanzitutto, richiedi un digest, che viene restituito come file di output e funge da firma dell'intera cronologia delle modifiche del registro. Quindi, richiedi una bozza della revisione relativa a quel digest. Utilizzando questa prova, l'integrità della revisione viene verificata se tutti i controlli di convalida vengono superati.

**Per verificare la revisione di un documento**

1. Esamina i seguenti `.ts` file, che contengono gli oggetti QLDB necessari per la verifica.

   1. `BlockAddress.ts`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      import { ValueHolder } from "aws-sdk/clients/qldb";
      import { dom, IonTypes } from "ion-js";
      
      export class BlockAddress {
          _strandId: string;
          _sequenceNo: number;
      
          constructor(strandId: string, sequenceNo: number) {
              this._strandId = strandId;
              this._sequenceNo = sequenceNo;
          }
      }
      
      /**
       * Convert a block address from an Ion value into a ValueHolder.
       * Shape of the ValueHolder must be: {'IonText': "{strandId: <"strandId">, sequenceNo: <sequenceNo>}"}
       * @param value The Ion value that contains the block address values to convert.
       * @returns The ValueHolder that contains the strandId and sequenceNo.
       */
      export function blockAddressToValueHolder(value: dom.Value): ValueHolder {
          const blockAddressValue : dom.Value = getBlockAddressValue(value);
          const strandId: string = getStrandId(blockAddressValue);
          const sequenceNo: number = getSequenceNo(blockAddressValue);
          const valueHolder: string = `{strandId: "${strandId}", sequenceNo: ${sequenceNo}}`;
          const blockAddress: ValueHolder = {IonText: valueHolder};
          return blockAddress;
      }
      
      /**
       * Helper method that to get the Metadata ID.
       * @param value The Ion value.
       * @returns The Metadata ID.
       */
      export function getMetadataId(value: dom.Value): string {
          const metaDataId: dom.Value = value.get("id");
          if (metaDataId === null) {
              throw new Error(`Expected field name id, but not found.`);
          }
          return metaDataId.stringValue();
      }
      
      /**
       * Helper method to get the Sequence No.
       * @param value The Ion value.
       * @returns The Sequence No.
       */
      export function getSequenceNo(value : dom.Value): number {
          const sequenceNo: dom.Value = value.get("sequenceNo");
          if (sequenceNo === null) {
              throw new Error(`Expected field name sequenceNo, but not found.`);
          }
          return sequenceNo.numberValue();
      }
      
      /**
       * Helper method to get the Strand ID.
       * @param value The Ion value.
       * @returns The Strand ID.
       */
      export function getStrandId(value: dom.Value): string {
          const strandId: dom.Value = value.get("strandId");
          if (strandId === null) {
              throw new Error(`Expected field name strandId, but not found.`);
          }
          return strandId.stringValue();
      }
      
      export function getBlockAddressValue(value: dom.Value) : dom.Value {
          const type = value.getType();
          if (type !== IonTypes.STRUCT) {
              throw new Error(`Unexpected format: expected struct, but got IonType: ${type.name}`);
          }
          const blockAddress: dom.Value = value.get("blockAddress");
          if (blockAddress == null) {
              throw new Error(`Expected field name blockAddress, but not found.`);
          }
          return blockAddress;
      }
      ```

   1. `Verifier.ts`

      ```
      /*
       * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
       * SPDX-License-Identifier: MIT-0
       *
       * Permission is hereby granted, free of charge, to any person obtaining a copy of this
       * software and associated documentation files (the "Software"), to deal in the Software
       * without restriction, including without limitation the rights to use, copy, modify,
       * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
       * permit persons to whom the Software is furnished to do so.
       *
       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       */
      
      import { Digest, ValueHolder } from "aws-sdk/clients/qldb";
      import { createHash } from "crypto";
      import { dom, toBase64 } from "ion-js";
      
      import { getBlobValue } from "./Util";
      
      const HASH_LENGTH: number = 32;
      const UPPER_BOUND: number = 8;
      
      /**
       * Build the candidate digest representing the entire ledger from the Proof hashes.
       * @param proof The Proof object.
       * @param leafHash The revision hash to pair with the first hash in the Proof hashes list.
       * @returns The calculated root hash.
       */
      function buildCandidateDigest(proof: ValueHolder, leafHash: Uint8Array): Uint8Array {
          const parsedProof: Uint8Array[] = parseProof(proof);
          const rootHash: Uint8Array = calculateRootHashFromInternalHash(parsedProof, leafHash);
          return rootHash;
      }
      
      /**
       * Combine the internal hashes and the leaf hash until only one root hash remains.
       * @param internalHashes An array of hash values.
       * @param leafHash The revision hash to pair with the first hash in the Proof hashes list.
       * @returns The root hash constructed by combining internal hashes.
       */
      function calculateRootHashFromInternalHash(internalHashes: Uint8Array[], leafHash: Uint8Array): Uint8Array {
          const rootHash: Uint8Array = internalHashes.reduce(joinHashesPairwise, leafHash);
          return rootHash;
      }
      
      /**
       * Compare two hash values by converting each Uint8Array byte, which is unsigned by default,
       * into a signed byte, assuming they are little endian.
       * @param hash1 The hash value to compare.
       * @param hash2 The hash value to compare.
       * @returns Zero if the hash values are equal, otherwise return the difference of the first pair of non-matching bytes.
       */
      function compareHashValues(hash1: Uint8Array, hash2: Uint8Array): number {
          if (hash1.length !== HASH_LENGTH || hash2.length !== HASH_LENGTH) {
              throw new Error("Invalid hash.");
          }
          for (let i = hash1.length-1; i >= 0; i--) {
              const difference: number = (hash1[i]<<24 >>24) - (hash2[i]<<24 >>24);
              if (difference !== 0) {
                  return difference;
              }
          }
          return 0;
      }
      
      /**
       * Helper method that concatenates two Uint8Array.
       * @param arrays List of array to concatenate, in the order provided.
       * @returns The concatenated array.
       */
      function concatenate(...arrays: Uint8Array[]): Uint8Array {
          let totalLength = 0;
          for (const arr of arrays) {
              totalLength += arr.length;
          }
          const result = new Uint8Array(totalLength);
          let offset = 0;
          for (const arr of arrays) {
              result.set(arr, offset);
              offset += arr.length;
          }
          return result;
      }
      
      /**
       * Flip a single random bit in the given hash value.
       * This method is intended to be used for purpose of demonstrating the QLDB verification features only.
       * @param original The hash value to alter.
       * @returns The altered hash with a single random bit changed.
       */
      export function flipRandomBit(original: any): Uint8Array {
          if (original.length === 0) {
              throw new Error("Array cannot be empty!");
          }
          const bytePos: number = Math.floor(Math.random() * original.length);
          const bitShift: number = Math.floor(Math.random() * UPPER_BOUND);
          const alteredHash: Uint8Array = original;
      
          alteredHash[bytePos] = alteredHash[bytePos] ^ (1 << bitShift);
          return alteredHash;
      }
      
      /**
       * Take two hash values, sort them, concatenate them, and generate a new hash value from the concatenated values.
       * @param h1 Byte array containing one of the hashes to compare.
       * @param h2 Byte array containing one of the hashes to compare.
       * @returns The concatenated array of hashes.
       */
      export function joinHashesPairwise(h1: Uint8Array, h2: Uint8Array): Uint8Array {
          if (h1.length === 0) {
              return h2;
          }
          if (h2.length === 0) {
              return h1;
          }
          let concat: Uint8Array;
          if (compareHashValues(h1, h2) < 0) {
              concat = concatenate(h1, h2);
          } else {
              concat = concatenate(h2, h1);
          }
          const hash = createHash('sha256');
          hash.update(concat);
          const newDigest: Uint8Array = hash.digest();
          return newDigest;
      }
      
      /**
       * Parse the Block object returned by QLDB and retrieve block hash.
       * @param valueHolder A structure containing an Ion string value.
       * @returns The block hash.
       */
      export function parseBlock(valueHolder: ValueHolder): Uint8Array {
          const block: dom.Value = dom.load(valueHolder.IonText);
          const blockHash: Uint8Array = getBlobValue(block, "blockHash");
          return blockHash;
      }
      
      /**
       * Parse the Proof object returned by QLDB into an iterator.
       * The Proof object returned by QLDB is a dictionary like the following:
       * {'IonText': '[{{<hash>}},{{<hash>}}]'}
       * @param valueHolder A structure containing an Ion string value.
       * @returns A list of hash values.
       */
      function parseProof(valueHolder: ValueHolder): Uint8Array[] {
          const proofs : dom.Value = dom.load(valueHolder.IonText);
          return proofs.elements().map(proof => proof.uInt8ArrayValue());
      }
      
      /**
       *  Verify document revision against the provided digest.
       * @param documentHash The SHA-256 value representing the document revision to be verified.
       * @param digest The SHA-256 hash value representing the ledger digest.
       * @param proof The Proof object retrieved from GetRevision.getRevision.
       * @returns If the document revision verifies against the ledger digest.
       */
      export function verifyDocument(documentHash: Uint8Array, digest: Digest, proof: ValueHolder): boolean {
          const candidateDigest = buildCandidateDigest(proof, documentHash);
          return (toBase64(<Uint8Array> digest) === toBase64(candidateDigest));
      }
      ```

1. Utilizzate due `.ts` programmi (`GetDigest.ts`e`GetRevision.ts`) per eseguire le seguenti operazioni:
   + Richiedete un nuovo digest dal `vehicle-registration` registro.
   + Richiedi una bozza per ogni revisione del documento con VIN `1N4AL11D75C109151` dalla tabella. `VehicleRegistration`
   + Verifica le revisioni utilizzando il digest restituito e verifica ricalcolando il digest.

   Il `GetDigest.ts` programma contiene il codice seguente.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QLDB } from "aws-sdk";
   import { GetDigestRequest, GetDigestResponse } from "aws-sdk/clients/qldb";
   
   import { LEDGER_NAME } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   import { digestResponseToString } from "./qldb/Util";
   
   /**
    * Get the digest of a ledger's journal.
    * @param ledgerName Name of the ledger to operate on.
    * @param qldbClient The QLDB control plane client to use.
    * @returns Promise which fulfills with a GetDigestResponse.
    */
   export async function getDigestResult(ledgerName: string, qldbClient: QLDB): Promise<GetDigestResponse> {
       const request: GetDigestRequest = {
           Name: ledgerName
       };
       const result: GetDigestResponse = await qldbClient.getDigest(request).promise();
       return result;
   }
   
   /**
    * This is an example for retrieving the digest of a particular ledger.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbClient: QLDB = new QLDB();
           log(`Retrieving the current digest for ledger: ${LEDGER_NAME}.`);
           const digest: GetDigestResponse = await getDigestResult(LEDGER_NAME, qldbClient);
           log(`Success. Ledger digest: \n${digestResponseToString(digest)}.`);
       } catch (e) {
           error(`Unable to get a ledger digest: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```
**Nota**  
Usa la `getDigest` funzione per richiedere un riassunto che copra la *punta* corrente del diario nel tuo libro mastro. Il tip of the journal si riferisce all'ultimo blocco eseguito nel momento in cui QLDB riceve la tua richiesta.

   Il `GetRevision.ts` programma contiene il seguente codice.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { QldbDriver, TransactionExecutor } from "amazon-qldb-driver-nodejs";
   import { QLDB } from "aws-sdk";
   import { Digest, GetDigestResponse, GetRevisionRequest, GetRevisionResponse, ValueHolder } from "aws-sdk/clients/qldb";
   import { dom, toBase64 } from "ion-js";
   
   import { getQldbDriver } from "./ConnectToLedger";
   import { getDigestResult } from './GetDigest';
   import { VEHICLE_REGISTRATION } from "./model/SampleData"
   import { blockAddressToValueHolder, getMetadataId } from './qldb/BlockAddress';
   import { LEDGER_NAME } from './qldb/Constants';
   import { error, log } from "./qldb/LogUtil";
   import { getBlobValue, valueHolderToString } from "./qldb/Util";
   import { flipRandomBit, verifyDocument } from "./qldb/Verifier";
   
   /**
    * Get the revision data object for a specified document ID and block address.
    * Also returns a proof of the specified revision for verification.
    * @param ledgerName Name of the ledger containing the document to query.
    * @param documentId Unique ID for the document to be verified, contained in the committed view of the document.
    * @param blockAddress The location of the block to request.
    * @param digestTipAddress The latest block location covered by the digest.
    * @param qldbClient The QLDB control plane client to use.
    * @returns Promise which fulfills with a GetRevisionResponse.
    */
   async function getRevision(
       ledgerName: string,
       documentId: string,
       blockAddress: ValueHolder,
       digestTipAddress: ValueHolder,
       qldbClient: QLDB
   ): Promise<GetRevisionResponse> {
       const request: GetRevisionRequest = {
           Name: ledgerName,
           BlockAddress: blockAddress,
           DocumentId: documentId,
           DigestTipAddress: digestTipAddress
       };
       const result: GetRevisionResponse = await qldbClient.getRevision(request).promise();
       return result;
   }
   
   /**
    * Query the table metadata for a particular vehicle for verification.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param vin VIN to query the table metadata of a specific registration with.
    * @returns Promise which fulfills with a list of Ion values that contains the results of the query.
    */
   export async function lookupRegistrationForVin(txn: TransactionExecutor, vin: string): Promise<dom.Value[]> {
       log(`Querying the 'VehicleRegistration' table for VIN: ${vin}...`);
       let resultList: dom.Value[];
       const query: string = "SELECT blockAddress, metadata.id FROM _ql_committed_VehicleRegistration WHERE data.VIN = ?";
   
       await txn.execute(query, vin).then(function(result) {
         resultList = result.getResultList();
       });
       return resultList;
   }
   
   /**
    * Verify each version of the registration for the given VIN.
    * @param txn The {@linkcode TransactionExecutor} for lambda execute.
    * @param ledgerName The ledger to get the digest from.
    * @param vin VIN to query the revision history of a specific registration with.
    * @param qldbClient The QLDB control plane client to use.
    * @returns Promise which fulfills with void.
    * @throws Error: When verification fails.
    */
   export async function verifyRegistration(
       txn: TransactionExecutor, 
       ledgerName: string, 
       vin: string,
       qldbClient: QLDB
   ): Promise<void> {
       log(`Let's verify the registration with VIN = ${vin}, in ledger = ${ledgerName}.`);
       const digest: GetDigestResponse = await getDigestResult(ledgerName, qldbClient);
       const digestBytes: Digest = digest.Digest;
       const digestTipAddress: ValueHolder = digest.DigestTipAddress;
   
       log(
           `Got a ledger digest: digest tip address = \n${valueHolderToString(digestTipAddress)},
           digest = \n${toBase64(<Uint8Array> digestBytes)}.`
       );
       log(`Querying the registration with VIN = ${vin} to verify each version of the registration...`);
       const resultList: dom.Value[] = await lookupRegistrationForVin(txn, vin);
       log("Getting a proof for the document.");
   
       for (const result of resultList) {
           const blockAddress: ValueHolder =  blockAddressToValueHolder(result);
           const documentId: string = getMetadataId(result);
   
           const revisionResponse: GetRevisionResponse = await getRevision(
               ledgerName, 
               documentId, 
               blockAddress, 
               digestTipAddress,
               qldbClient
           );
   
           const revision: dom.Value = dom.load(revisionResponse.Revision.IonText);
           const documentHash: Uint8Array = getBlobValue(revision, "hash");
           const proof: ValueHolder = revisionResponse.Proof;
           log(`Got back a proof: ${valueHolderToString(proof)}.`);
   
           let verified: boolean = verifyDocument(documentHash, digestBytes, proof);
           if (!verified) {
              throw new Error("Document revision is not verified.");
           } else {
               log("Success! The document is verified.");
           }
           const alteredDocumentHash: Uint8Array = flipRandomBit(documentHash);
   
           log(
               `Flipping one bit in the document's hash and assert that the document is NOT verified.
               The altered document hash is: ${toBase64(alteredDocumentHash)}`
           );
           verified = verifyDocument(alteredDocumentHash, digestBytes, proof);
   
           if (verified) {
               throw new Error("Expected altered document hash to not be verified against digest.");
           } else {
               log("Success! As expected flipping a bit in the document hash causes verification to fail.");
           }
           log(`Finished verifying the registration with VIN = ${vin} in ledger = ${ledgerName}.`);
       }
   }
   
   /**
    * Verify the integrity of a document revision in a QLDB ledger.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbClient: QLDB = new QLDB();
           const qldbDriver: QldbDriver = getQldbDriver();
   
           const registration = VEHICLE_REGISTRATION[0];
           const vin: string = registration.VIN;
   
           await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
               await verifyRegistration(txn, LEDGER_NAME, vin, qldbClient);
           });
       } catch (e) {
           error(`Unable to verify revision: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```
**Nota**  
Dopo che la `getRevision` funzione ha restituito una bozza per la revisione del documento specificata, questo programma utilizza un'API lato client per verificare tale revisione.

1. Per eseguire il programma transpiled, immettete il seguente comando.

   ```
   node dist/GetRevision.js
   ```

Se non hai più bisogno di usare il `vehicle-registration` libro mastro, procedi a. [Passaggio 8 (opzionale): Pulisci le risorse](getting-started.nodejs.step-8.md)

# Passaggio 8 (opzionale): Pulisci le risorse
Fase 8: Pulizia

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Puoi `vehicle-registration` continuare a utilizzare il registro. Tuttavia, se non è più necessario, è necessario eliminarlo.

**Per eliminare il libro mastro**

1. Utilizzate il seguente programma (`DeleteLedger.ts`) per eliminare il `vehicle-registration` registro e tutto il suo contenuto.

   ```
   /*
    * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    * SPDX-License-Identifier: MIT-0
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    * software and associated documentation files (the "Software"), to deal in the Software
    * without restriction, including without limitation the rights to use, copy, modify,
    * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    * permit persons to whom the Software is furnished to do so.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
   
   import { isResourceNotFoundException } from "amazon-qldb-driver-nodejs";
   import { AWSError, QLDB } from "aws-sdk";
   import { DeleteLedgerRequest, DescribeLedgerRequest } from "aws-sdk/clients/qldb";
   
   import { setDeletionProtection } from "./DeletionProtection";
   import { LEDGER_NAME } from "./qldb/Constants";
   import { error, log } from "./qldb/LogUtil";
   import { sleep } from "./qldb/Util";
   
   const LEDGER_DELETION_POLL_PERIOD_MS = 20000;
   
   /**
    * Send a request to QLDB to delete the specified ledger.
    * @param ledgerName Name of the ledger to be deleted.
    * @param qldbClient The QLDB control plane client to use.
    * @returns Promise which fulfills with void.
    */
   export async function deleteLedger(ledgerName: string, qldbClient: QLDB): Promise<void> {
       log(`Attempting to delete the ledger with name: ${ledgerName}`);
       const request: DeleteLedgerRequest = {
           Name: ledgerName
       };
       await qldbClient.deleteLedger(request).promise();
       log("Success.");
   }
   
   /**
    * Wait for the ledger to be deleted.
    * @param ledgerName Name of the ledger to be deleted.
    * @param qldbClient The QLDB control plane client to use.
    * @returns Promise which fulfills with void.
    */
   export async function waitForDeleted(ledgerName: string, qldbClient: QLDB): Promise<void> {
       log("Waiting for the ledger to be deleted...");
       const request: DescribeLedgerRequest = {
           Name: ledgerName
       };
       let isDeleted: boolean = false;
       while (true) {
           await qldbClient.describeLedger(request).promise().catch((error: AWSError) => {
               if (isResourceNotFoundException(error)) {
                   isDeleted = true;
                   log("Success. Ledger is deleted.");
               }
           });
           if (isDeleted) {
               break;
           }
           log("The ledger is still being deleted. Please wait...");
           await sleep(LEDGER_DELETION_POLL_PERIOD_MS);
       }
   }
   
   /**
    * Delete a ledger.
    * @returns Promise which fulfills with void.
    */
   const main = async function(): Promise<void> {
       try {
           const qldbClient: QLDB = new QLDB();
           await setDeletionProtection(LEDGER_NAME, qldbClient, false);
           await deleteLedger(LEDGER_NAME, qldbClient);
           await waitForDeleted(LEDGER_NAME, qldbClient);
       } catch (e) {
           error(`Unable to delete the ledger: ${e}`);
       }
   }
   
   if (require.main === module) {
       main();
   }
   ```
**Nota**  
Se la protezione da eliminazione è abilitata per il registro, devi prima disabilitarla prima di poter eliminare il registro utilizzando l'API QLDB.

1. Per eseguire il programma transpiled, inserisci il seguente comando.

   ```
   node dist/DeleteLedger.js
   ```

# Tutorial su Amazon QLDB Python
Tutorial di Python

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questa implementazione dell'applicazione di esempio del tutorial, utilizzi il driver Amazon QLDB con AWS SDK per Python (Boto3) per creare un registro QLDB e compilarlo con dati di esempio.

Mentre segui questo tutorial, puoi fare riferimento al client di [basso livello QLDB](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/qldb.html) nell'*SDK for Python (Boto3) API Reference per AWS le* operazioni dell'API di gestione. Per le operazioni sui dati transazionali, puoi fare riferimento al [QLDB Driver for](https://amazon-qldb-driver-python.readthedocs.io/en/stable/) Python API Reference.

**Nota**  
Ove applicabile, alcuni passaggi del tutorial contengono comandi o esempi di codice diversi per ogni versione principale supportata del driver QLDB per Python.

**Topics**
+ [

# Installazione dell'applicazione di esempio Amazon QLDB Python
](sample-app.python.md)
+ [

# Fase 1: Creare un nuovo registro
](getting-started.python.step-1.md)
+ [

# Passaggio 2: verifica la connettività al registro
](getting-started.python.step-2.md)
+ [

# Passaggio 3: Creare tabelle, indici e dati di esempio
](getting-started.python.step-3.md)
+ [

# Passaggio 4: interrogare le tabelle in un libro mastro
](getting-started.python.step-4.md)
+ [

# Fase 5: Modificare i documenti in un libro mastro
](getting-started.python.step-5.md)
+ [

# Fase 6: Visualizzare la cronologia delle revisioni di un documento
](getting-started.python.step-6.md)
+ [

# Fase 7: Verificare un documento in un libro mastro
](getting-started.python.step-7.md)
+ [

# Passaggio 8 (opzionale): Pulisci le risorse
](getting-started.python.step-8.md)

# Installazione dell'applicazione di esempio Amazon QLDB Python
Installazione dell'applicazione di esempio

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Questa sezione descrive come installare ed eseguire l'applicazione di esempio Amazon QLDB fornita per il step-by-step tutorial Python. Il caso d'uso di questa applicazione di esempio è un database del Dipartimento dei veicoli a motore (DMV) che tiene traccia delle informazioni storiche complete sulle immatricolazioni dei veicoli.

[L'applicazione di esempio DMV per Python è open source nel repository aws-samples/ -python. GitHub amazon-qldb-dmv-sample](https://github.com/aws-samples/amazon-qldb-dmv-sample-python)

## Prerequisiti


Prima di iniziare, assicurati di completare il driver QLDB per Python. [Prerequisiti](getting-started.python.md#getting-started.python.prereqs) Ciò include l'installazione di Python e le seguenti operazioni:

1. Iscriviti a. AWS

1. Crea un utente con le autorizzazioni QLDB appropriate.

1. Concedi l'accesso programmatico per lo sviluppo.

Per completare tutti i passaggi di questo tutorial, è necessario l'accesso amministrativo completo alla risorsa di registro tramite l'API QLDB.

## Installazione


**Per installare l'applicazione di esempio**

1. Immettete il seguente `pip` comando per clonare e installare l'applicazione di esempio da GitHub.

------
#### [ 3.x ]

   ```
   pip install git+https://github.com/aws-samples/amazon-qldb-dmv-sample-python.git
   ```

------
#### [ 2.x ]

   ```
   pip install git+https://github.com/aws-samples/amazon-qldb-dmv-sample-python.git@v1.0.0
   ```

------

   L'applicazione di esempio racchiude il codice sorgente completo di questo tutorial e le sue dipendenze, inclusi il driver Python e il. [AWS SDK per Python (Boto3)](https://aws.amazon.com/sdk-for-python)

1. Prima di iniziare a eseguire il codice dalla riga di comando, spostate la directory di lavoro corrente nella posizione in cui è installato il `pyqldbsamples` pacchetto. Inserire il seguente comando.

   ```
   cd $(python -c "import pyqldbsamples; print(pyqldbsamples.__path__[0])")
   ```

1. Procedi con [Fase 1: Creare un nuovo registro](getting-started.python.step-1.md) l'avvio del tutorial e la creazione di un libro mastro.

# Fase 1: Creare un nuovo registro


**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo passaggio, crei un nuovo registro Amazon QLDB denominato. `vehicle-registration`

**Per creare un nuovo registro**

1. Esamina il seguente file (`constants.py`), che contiene valori costanti utilizzati da tutti gli altri programmi di questo tutorial.

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   
   
   class Constants:
       """
       Constant values used throughout this tutorial.
       """
       LEDGER_NAME = "vehicle-registration"
   
       VEHICLE_REGISTRATION_TABLE_NAME = "VehicleRegistration"
       VEHICLE_TABLE_NAME = "Vehicle"
       PERSON_TABLE_NAME = "Person"
       DRIVERS_LICENSE_TABLE_NAME = "DriversLicense"
   
       LICENSE_NUMBER_INDEX_NAME = "LicenseNumber"
       GOV_ID_INDEX_NAME = "GovId"
       VEHICLE_VIN_INDEX_NAME = "VIN"
       LICENSE_PLATE_NUMBER_INDEX_NAME = "LicensePlateNumber"
       PERSON_ID_INDEX_NAME = "PersonId"
   
       JOURNAL_EXPORT_S3_BUCKET_NAME_PREFIX = "qldb-tutorial-journal-export"
       USER_TABLES = "information_schema.user_tables"
       S3_BUCKET_ARN_TEMPLATE = "arn:aws:s3:::"
       LEDGER_NAME_WITH_TAGS = "tags"
   
       RETRY_LIMIT = 4
   ```

1. Utilizzate il seguente programma (`create_ledger.py`) per creare un registro denominato`vehicle-registration`.

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   from time import sleep
   
   from boto3 import client
   
   from pyqldbsamples.constants import Constants
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   qldb_client = client('qldb')
   
   LEDGER_CREATION_POLL_PERIOD_SEC = 10
   ACTIVE_STATE = "ACTIVE"
   
   
   def create_ledger(name):
       """
       Create a new ledger with the specified name.
   
       :type name: str
       :param name: Name for the ledger to be created.
   
       :rtype: dict
       :return: Result from the request.
       """
       logger.info("Let's create the ledger named: {}...".format(name))
       result = qldb_client.create_ledger(Name=name, PermissionsMode='ALLOW_ALL')
       logger.info('Success. Ledger state: {}.'.format(result.get('State')))
       return result
   
   
   def wait_for_active(name):
       """
       Wait for the newly created ledger to become active.
   
       :type name: str
       :param name: The ledger to check on.
   
       :rtype: dict
       :return: Result from the request.
       """
       logger.info('Waiting for ledger to become active...')
       while True:
           result = qldb_client.describe_ledger(Name=name)
           if result.get('State') == ACTIVE_STATE:
               logger.info('Success. Ledger is active and ready to use.')
               return result
           logger.info('The ledger is still creating. Please wait...')
           sleep(LEDGER_CREATION_POLL_PERIOD_SEC)
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Create a ledger and wait for it to be active.
       """
       try:
           create_ledger(ledger_name)
           wait_for_active(ledger_name)
       except Exception as e:
           logger.exception('Unable to create the ledger!')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```
**Nota**  
Nella `create_ledger` chiamata, è necessario specificare un nome di registro e una modalità di autorizzazione. Ti consigliamo di utilizzare la modalità `STANDARD` autorizzazioni per massimizzare la sicurezza dei dati del registro.
Quando si crea un registro, la *protezione da eliminazione* è abilitata per impostazione predefinita. Questa è una funzionalità di QLDB che impedisce l'eliminazione dei registri da parte di qualsiasi utente. Hai la possibilità di disabilitare la protezione da eliminazione durante la creazione del registro utilizzando l'API QLDB o il (). AWS Command Line Interface AWS CLI
Facoltativamente, puoi anche specificare i tag da allegare al tuo libro mastro.

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python create_ledger.py
   ```

Per verificare la connessione al nuovo registro, procedi a. [Passaggio 2: verifica la connettività al registro](getting-started.python.step-2.md)

# Passaggio 2: verifica la connettività al registro
Fase 2: Verificare la connettività

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

In questo passaggio, verifichi di poterti connettere al `vehicle-registration` registro in Amazon QLDB utilizzando l'endpoint API dei dati transazionali.

**Per testare la connettività al registro**

1. Utilizzate il seguente programma (`connect_to_ledger.py`) per creare una connessione di sessione dati al `vehicle-registration` registro.

------
#### [ 3.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from botocore.exceptions import ClientError
   
   from pyqldb.driver.qldb_driver import QldbDriver
   from pyqldbsamples.constants import Constants
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def create_qldb_driver(ledger_name=Constants.LEDGER_NAME, region_name=None, endpoint_url=None, boto3_session=None):
       """
       Create a QLDB driver for executing transactions.
   
       :type ledger_name: str
       :param ledger_name: The QLDB ledger name.
   
       :type region_name: str
       :param region_name: See [1].
   
       :type endpoint_url: str
       :param endpoint_url: See [1].
   
       :type boto3_session: :py:class:`boto3.session.Session`
       :param boto3_session: The boto3 session to create the client with (see [1]).
   
       :rtype: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :return: A QLDB driver object.
   
       [1]: `Boto3 Session.client Reference <https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html#boto3.session.Session.client>`.
       """
       qldb_driver = QldbDriver(ledger_name=ledger_name, region_name=region_name, endpoint_url=endpoint_url,
                                boto3_session=boto3_session)
       return qldb_driver
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Connect to a given ledger using default settings.
       """
       try:
           with create_qldb_driver(ledger_name) as driver:
               logger.info('Listing table names ')
               for table in driver.list_tables():
                   logger.info(table)
       except ClientError as ce:
           logger.exception('Unable to list tables.')
           raise ce
   
   
   if __name__ == '__main__':
       main()
   ```

**Nota**  
Per eseguire transazioni di dati sul registro, è necessario creare un oggetto driver QLDB per connettersi a un registro specifico. Si tratta di un oggetto client diverso dall'`qldb_client`oggetto utilizzato nel passaggio precedente per creare il registro. Quel client precedente viene utilizzato solo per eseguire le operazioni dell'API di gestione elencate in. [Riferimento all'API Amazon QLDB](api-reference.md)
È necessario specificare un nome contabile quando si crea questo oggetto driver.

------
#### [ 2.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from botocore.exceptions import ClientError
   
   from pyqldb.driver.pooled_qldb_driver import PooledQldbDriver
   from pyqldbsamples.constants import Constants
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def create_qldb_driver(ledger_name=Constants.LEDGER_NAME, region_name=None, endpoint_url=None, boto3_session=None):
       """
       Create a QLDB driver for creating sessions.
   
       :type ledger_name: str
       :param ledger_name: The QLDB ledger name.
   
       :type region_name: str
       :param region_name: See [1].
   
       :type endpoint_url: str
       :param endpoint_url: See [1].
   
       :type boto3_session: :py:class:`boto3.session.Session`
       :param boto3_session: The boto3 session to create the client with (see [1]).
   
       :rtype: :py:class:`pyqldb.driver.pooled_qldb_driver.PooledQldbDriver`
       :return: A pooled QLDB driver object.
   
       [1]: `Boto3 Session.client Reference <https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html#boto3.session.Session.client>`.
       """
       qldb_driver = PooledQldbDriver(ledger_name=ledger_name, region_name=region_name, endpoint_url=endpoint_url,
                                      boto3_session=boto3_session)
       return qldb_driver
   
   
   def create_qldb_session():
       """
       Retrieve a QLDB session object.
   
       :rtype: :py:class:`pyqldb.session.pooled_qldb_session.PooledQldbSession`
       :return: A pooled QLDB session object.
       """
       qldb_session = pooled_qldb_driver.get_session()
       return qldb_session
   
   
   pooled_qldb_driver = create_qldb_driver()
   
   
   if __name__ == '__main__':
       """
       Connect to a session for a given ledger using default settings.
       """
       try:
           qldb_session = create_qldb_session()
           logger.info('Listing table names ')
           for table in qldb_session.list_tables():
               logger.info(table)
       except ClientError:
           logger.exception('Unable to create session.')
   ```

**Nota**  
Per eseguire transazioni di dati sul registro, è necessario creare un oggetto driver QLDB per connettersi a un registro specifico. Si tratta di un oggetto client diverso dall'`qldb_client`oggetto utilizzato nel passaggio precedente per creare il registro. Quel client precedente viene utilizzato solo per eseguire le operazioni dell'API di gestione elencate in. [Riferimento all'API Amazon QLDB](api-reference.md)
Innanzitutto, crea un oggetto driver QLDB in pool. È necessario specificare un nome contabile quando si crea questo driver.
Quindi, è possibile creare sessioni da questo oggetto driver condiviso.

------

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python connect_to_ledger.py
   ```

Per creare tabelle nel `vehicle-registration` registro, procedi a. [Passaggio 3: Creare tabelle, indici e dati di esempio](getting-started.python.step-3.md)

# Passaggio 3: Creare tabelle, indici e dati di esempio


**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Quando il tuo registro Amazon QLDB è attivo e accetta connessioni, puoi iniziare a creare tabelle per i dati sui veicoli, i loro proprietari e le loro informazioni di immatricolazione. Dopo aver creato le tabelle e gli indici, puoi caricarli con i dati.

In questo passaggio, crei quattro tabelle nel libro mastro: `vehicle-registration`
+ `VehicleRegistration`
+ `Vehicle`
+ `Person`
+ `DriversLicense`

Vengono inoltre creati i seguenti indici.


****  

| Nome tabella | Campo | 
| --- | --- | 
| VehicleRegistration | VIN | 
| VehicleRegistration | LicensePlateNumber | 
| Vehicle | VIN | 
| Person | GovId | 
| DriversLicense | LicenseNumber | 
| DriversLicense | PersonId | 

Quando si inseriscono dati di esempio, si inseriscono innanzitutto i documenti nella tabella. `Person` Quindi, si utilizzano i dati assegnati `id` dal sistema a ciascun `Person` documento per compilare i campi corrispondenti nei documenti e nei documenti appropriati. `VehicleRegistration` `DriversLicense`

**Suggerimento**  
Come procedura ottimale, utilizzate il sistema assegnato a un documento come chiave esterna`id`. Sebbene sia possibile definire campi destinati a essere identificatori univoci (ad esempio, il VIN di un veicolo), il vero identificatore univoco di un documento è il suo. `id` Questo campo è incluso nei metadati del documento, a cui è possibile eseguire una query nella visualizzazione confermata (*la vista* di una tabella definita dal sistema).  
Per ulteriori informazioni sulle viste in QLDB, vedere. [Concetti principali](ledger-structure.md) Per ulteriori informazioni sui metadati, consulta. [Interrogazione dei metadati dei documenti](working.metadata.md)

**Per creare tabelle e indici**

1. Utilizzate il seguente programma (`create_table.py`) per creare le tabelle menzionate in precedenza.

------
#### [ 3.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_driver
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def create_table(driver, table_name):
       """
       Create a table with the specified name.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type table_name: str
       :param table_name: Name of the table to create.
   
       :rtype: int
       :return: The number of changes to the database.
       """
       logger.info("Creating the '{}' table...".format(table_name))
       statement = 'CREATE TABLE {}'.format(table_name)
       cursor = driver.execute_lambda(lambda executor: executor.execute_statement(statement))
       logger.info('{} table created successfully.'.format(table_name))
       return len(list(cursor))
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Create registrations, vehicles, owners, and licenses tables.
       """
       try:
           with create_qldb_driver(ledger_name) as driver:
               create_table(driver, Constants.DRIVERS_LICENSE_TABLE_NAME)
               create_table(driver, Constants.PERSON_TABLE_NAME)
               create_table(driver, Constants.VEHICLE_TABLE_NAME)
               create_table(driver, Constants.VEHICLE_REGISTRATION_TABLE_NAME)
               logger.info('Tables created successfully.')
       except Exception as e:
           logger.exception('Errors creating tables.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```

------
#### [ 2.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_session
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def create_table(transaction_executor, table_name):
       """
       Create a table with the specified name using an Executor object.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type table_name: str
       :param table_name: Name of the table to create.
   
       :rtype: int
       :return: The number of changes to the database.
       """
       logger.info("Creating the '{}' table...".format(table_name))
       statement = 'CREATE TABLE {}'.format(table_name)
       cursor = transaction_executor.execute_statement(statement)
       logger.info('{} table created successfully.'.format(table_name))
       return len(list(cursor))
   
   
   if __name__ == '__main__':
       """
       Create registrations, vehicles, owners, and licenses tables in a single transaction.
       """
       try:
           with create_qldb_session() as session:
               session.execute_lambda(lambda x: create_table(x, Constants.DRIVERS_LICENSE_TABLE_NAME) and
                                      create_table(x, Constants.PERSON_TABLE_NAME) and
                                      create_table(x, Constants.VEHICLE_TABLE_NAME) and
                                      create_table(x, Constants.VEHICLE_REGISTRATION_TABLE_NAME),
                                      lambda retry_attempt: logger.info('Retrying due to OCC conflict...'))
               logger.info('Tables created successfully.')
       except Exception:
           logger.exception('Errors creating tables.')
   ```

------
**Nota**  
Questo programma dimostra come utilizzare la `execute_lambda` funzione. In questo esempio, esegui più istruzioni `CREATE TABLE` PartiQL con una singola espressione lambda.  
Questa funzione di esecuzione avvia implicitamente una transazione, esegue tutte le istruzioni in lambda e quindi esegue automaticamente il commit della transazione.

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python create_table.py
   ```

1. Utilizzate il seguente programma (`create_index.py`) per creare indici sulle tabelle, come descritto in precedenza.

------
#### [ 3.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_driver
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def create_index(driver, table_name, index_attribute):
       """
       Create an index for a particular table.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type table_name: str
       :param table_name: Name of the table to add indexes for.
   
       :type index_attribute: str
       :param index_attribute: Index to create on a single attribute.
   
       :rtype: int
       :return: The number of changes to the database.
       """
       logger.info("Creating index on '{}'...".format(index_attribute))
       statement = 'CREATE INDEX on {} ({})'.format(table_name, index_attribute)
       cursor = driver.execute_lambda(lambda executor: executor.execute_statement(statement))
       return len(list(cursor))
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Create indexes on tables in a particular ledger.
       """
       logger.info('Creating indexes on all tables...')
       try:
           with create_qldb_driver(ledger_name) as driver:
               create_index(driver, Constants.PERSON_TABLE_NAME, Constants.GOV_ID_INDEX_NAME)
               create_index(driver, Constants.VEHICLE_TABLE_NAME, Constants.VEHICLE_VIN_INDEX_NAME)
               create_index(driver, Constants.VEHICLE_REGISTRATION_TABLE_NAME, Constants.LICENSE_PLATE_NUMBER_INDEX_NAME)
               create_index(driver, Constants.VEHICLE_REGISTRATION_TABLE_NAME, Constants.VEHICLE_VIN_INDEX_NAME)
               create_index(driver, Constants.DRIVERS_LICENSE_TABLE_NAME, Constants.PERSON_ID_INDEX_NAME)
               create_index(driver, Constants.DRIVERS_LICENSE_TABLE_NAME, Constants.LICENSE_NUMBER_INDEX_NAME)
               logger.info('Indexes created successfully.')
       except Exception as e:
           logger.exception('Unable to create indexes.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```

------
#### [ 2.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_session
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def create_index(transaction_executor, table_name, index_attribute):
       """
       Create an index for a particular table.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type table_name: str
       :param table_name: Name of the table to add indexes for.
   
       :type index_attribute: str
       :param index_attribute: Index to create on a single attribute.
   
       :rtype: int
       :return: The number of changes to the database.
       """
       logger.info("Creating index on '{}'...".format(index_attribute))
       statement = 'CREATE INDEX on {} ({})'.format(table_name, index_attribute)
       cursor = transaction_executor.execute_statement(statement)
       return len(list(cursor))
   
   
   if __name__ == '__main__':
       """
       Create indexes on tables in a particular ledger.
       """
       logger.info('Creating indexes on all tables in a single transaction...')
       try:
           with create_qldb_session() as session:
               session.execute_lambda(lambda x: create_index(x, Constants.PERSON_TABLE_NAME,
                                                             Constants.GOV_ID_INDEX_NAME)
                                      and create_index(x, Constants.VEHICLE_TABLE_NAME,
                                                       Constants.VEHICLE_VIN_INDEX_NAME)
                                      and create_index(x, Constants.VEHICLE_REGISTRATION_TABLE_NAME,
                                                       Constants.LICENSE_PLATE_NUMBER_INDEX_NAME)
                                      and create_index(x, Constants.VEHICLE_REGISTRATION_TABLE_NAME,
                                                       Constants.VEHICLE_VIN_INDEX_NAME)
                                      and create_index(x, Constants.DRIVERS_LICENSE_TABLE_NAME,
                                                       Constants.PERSON_ID_INDEX_NAME)
                                      and create_index(x, Constants.DRIVERS_LICENSE_TABLE_NAME,
                                                       Constants.LICENSE_NUMBER_INDEX_NAME),
                                      lambda retry_attempt: logger.info('Retrying due to OCC conflict...'))
               logger.info('Indexes created successfully.')
       except Exception:
           logger.exception('Unable to create indexes.')
   ```

------

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python create_index.py
   ```

**Per caricare i dati nelle tabelle**

1. Esaminate il seguente file (`sample_data.py`), che rappresenta i dati di esempio che inserite nelle `vehicle-registration` tabelle. Questo file viene inoltre importato dal `amazon.ion` pacchetto per fornire funzioni di supporto che convertono, analizzano e stampano i dati di [Amazon Ion.](ion.md) 

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   from datetime import datetime
   from decimal import Decimal
   from logging import basicConfig, getLogger, INFO
   
   from amazon.ion.simple_types import IonPyBool, IonPyBytes, IonPyDecimal, IonPyDict, IonPyFloat, IonPyInt, IonPyList, \
       IonPyNull, IonPySymbol, IonPyText, IonPyTimestamp
   from amazon.ion.simpleion import dumps, loads
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   IonValue = (IonPyBool, IonPyBytes, IonPyDecimal, IonPyDict, IonPyFloat, IonPyInt, IonPyList, IonPyNull, IonPySymbol,
               IonPyText, IonPyTimestamp)
   
   
   class SampleData:
       """
       Sample domain objects for use throughout this tutorial.
       """
       DRIVERS_LICENSE = [
           {
               'PersonId': '',
               'LicenseNumber': 'LEWISR261LL',
               'LicenseType': 'Learner',
               'ValidFromDate': datetime(2016, 12, 20),
               'ValidToDate': datetime(2020, 11, 15)
           },
           {
               'PersonId': '',
               'LicenseNumber': 'LOGANB486CG',
               'LicenseType': 'Probationary',
               'ValidFromDate': datetime(2016, 4, 6),
               'ValidToDate': datetime(2020, 11, 15)
           },
           {
               'PersonId': '',
               'LicenseNumber': '744 849 301',
               'LicenseType': 'Full',
               'ValidFromDate': datetime(2017, 12, 6),
               'ValidToDate': datetime(2022, 10, 15)
           },
           {
               'PersonId': '',
               'LicenseNumber': 'P626-168-229-765',
               'LicenseType': 'Learner',
               'ValidFromDate': datetime(2017, 8, 16),
               'ValidToDate': datetime(2021, 11, 15)
           },
           {
               'PersonId': '',
               'LicenseNumber': 'S152-780-97-415-0',
               'LicenseType': 'Probationary',
               'ValidFromDate': datetime(2015, 8, 15),
               'ValidToDate': datetime(2021, 8, 21)
           }
       ]
       PERSON = [
           {
               'FirstName': 'Raul',
               'LastName': 'Lewis',
               'Address': '1719 University Street, Seattle, WA, 98109',
               'DOB': datetime(1963, 8, 19),
               'GovId': 'LEWISR261LL',
               'GovIdType': 'Driver License'
           },
           {
               'FirstName': 'Brent',
               'LastName': 'Logan',
               'DOB': datetime(1967, 7, 3),
               'Address': '43 Stockert Hollow Road, Everett, WA, 98203',
               'GovId': 'LOGANB486CG',
               'GovIdType': 'Driver License'
           },
           {
               'FirstName': 'Alexis',
               'LastName': 'Pena',
               'DOB': datetime(1974, 2, 10),
               'Address': '4058 Melrose Street, Spokane Valley, WA, 99206',
               'GovId': '744 849 301',
               'GovIdType': 'SSN'
           },
           {
               'FirstName': 'Melvin',
               'LastName': 'Parker',
               'DOB': datetime(1976, 5, 22),
               'Address': '4362 Ryder Avenue, Seattle, WA, 98101',
               'GovId': 'P626-168-229-765',
               'GovIdType': 'Passport'
           },
           {
               'FirstName': 'Salvatore',
               'LastName': 'Spencer',
               'DOB': datetime(1997, 11, 15),
               'Address': '4450 Honeysuckle Lane, Seattle, WA, 98101',
               'GovId': 'S152-780-97-415-0',
               'GovIdType': 'Passport'
           }
       ]
       VEHICLE = [
           {
               'VIN': '1N4AL11D75C109151',
               'Type': 'Sedan',
               'Year': 2011,
               'Make': 'Audi',
               'Model': 'A5',
               'Color': 'Silver'
           },
           {
               'VIN': 'KM8SRDHF6EU074761',
               'Type': 'Sedan',
               'Year': 2015,
               'Make': 'Tesla',
               'Model': 'Model S',
               'Color': 'Blue'
           },
           {
               'VIN': '3HGGK5G53FM761765',
               'Type': 'Motorcycle',
               'Year': 2011,
               'Make': 'Ducati',
               'Model': 'Monster 1200',
               'Color': 'Yellow'
           },
           {
               'VIN': '1HVBBAANXWH544237',
               'Type': 'Semi',
               'Year': 2009,
               'Make': 'Ford',
               'Model': 'F 150',
               'Color': 'Black'
           },
           {
               'VIN': '1C4RJFAG0FC625797',
               'Type': 'Sedan',
               'Year': 2019,
               'Make': 'Mercedes',
               'Model': 'CLK 350',
               'Color': 'White'
           }
       ]
       VEHICLE_REGISTRATION = [
           {
               'VIN': '1N4AL11D75C109151',
               'LicensePlateNumber': 'LEWISR261LL',
               'State': 'WA',
               'City': 'Seattle',
               'ValidFromDate': datetime(2017, 8, 21),
               'ValidToDate': datetime(2020, 5, 11),
               'PendingPenaltyTicketAmount': Decimal('90.25'),
               'Owners': {
                   'PrimaryOwner': {'PersonId': ''},
                   'SecondaryOwners': []
               }
           },
           {
               'VIN': 'KM8SRDHF6EU074761',
               'LicensePlateNumber': 'CA762X',
               'State': 'WA',
               'City': 'Kent',
               'PendingPenaltyTicketAmount': Decimal('130.75'),
               'ValidFromDate': datetime(2017, 9, 14),
               'ValidToDate': datetime(2020, 6, 25),
               'Owners': {
                   'PrimaryOwner': {'PersonId': ''},
                   'SecondaryOwners': []
               }
           },
           {
               'VIN': '3HGGK5G53FM761765',
               'LicensePlateNumber': 'CD820Z',
               'State': 'WA',
               'City': 'Everett',
               'PendingPenaltyTicketAmount': Decimal('442.30'),
               'ValidFromDate': datetime(2011, 3, 17),
               'ValidToDate': datetime(2021, 3, 24),
               'Owners': {
                   'PrimaryOwner': {'PersonId': ''},
                   'SecondaryOwners': []
               }
           },
           {
               'VIN': '1HVBBAANXWH544237',
               'LicensePlateNumber': 'LS477D',
               'State': 'WA',
               'City': 'Tacoma',
               'PendingPenaltyTicketAmount': Decimal('42.20'),
               'ValidFromDate': datetime(2011, 10, 26),
               'ValidToDate': datetime(2023, 9, 25),
               'Owners': {
                   'PrimaryOwner': {'PersonId': ''},
                   'SecondaryOwners': []
               }
           },
           {
               'VIN': '1C4RJFAG0FC625797',
               'LicensePlateNumber': 'TH393F',
               'State': 'WA',
               'City': 'Olympia',
               'PendingPenaltyTicketAmount': Decimal('30.45'),
               'ValidFromDate': datetime(2013, 9, 2),
               'ValidToDate': datetime(2024, 3, 19),
               'Owners': {
                   'PrimaryOwner': {'PersonId': ''},
                   'SecondaryOwners': []
               }
           }
       ]
   
   
   def convert_object_to_ion(py_object):
       """
       Convert a Python object into an Ion object.
   
       :type py_object: object
       :param py_object: The object to convert.
   
       :rtype: :py:class:`amazon.ion.simple_types.IonPyValue`
       :return: The converted Ion object.
       """
       ion_object = loads(dumps(py_object))
       return ion_object
   
   
   def to_ion_struct(key, value):
       """
       Convert the given key and value into an Ion struct.
   
       :type key: str
       :param key: The key which serves as an unique identifier.
   
       :type value: str
       :param value: The value associated with a given key.
   
       :rtype: :py:class:`amazon.ion.simple_types.IonPyDict`
       :return: The Ion dictionary object.
       """
       ion_struct = dict()
       ion_struct[key] = value
       return loads(str(ion_struct))
   
   
   def get_document_ids(transaction_executor, table_name, field, value):
       """
       Gets the document IDs from the given table.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type table_name: str
       :param table_name: The table name to query.
   
       :type field: str
       :param field: A field to query.
   
       :type value: str
       :param value: The key of the given field.
   
       :rtype: list
       :return: A list of document IDs.
       """
       query = "SELECT id FROM {} AS t BY id WHERE t.{} = ?".format(table_name, field)
       cursor = transaction_executor.execute_statement(query, convert_object_to_ion(value))
       return list(map(lambda table: table.get('id'), cursor))
   
   
   def get_document_ids_from_dml_results(result):
       """
       Return a list of modified document IDs as strings from DML results.
   
       :type result: :py:class:`pyqldb.cursor.buffered_cursor.BufferedCursor`
       :param: result: The result set from DML operation.
   
       :rtype: list
       :return: List of document IDs.
       """
       ret_val = list(map(lambda x: x.get('documentId'), result))
       return ret_val
   
   
   def print_result(cursor):
       """
       Pretty print the result set. Returns the number of documents in the result set.
   
       :type cursor: :py:class:`pyqldb.cursor.stream_cursor.StreamCursor`/
                     :py:class:`pyqldb.cursor.buffered_cursor.BufferedCursor`
       :param cursor: An instance of the StreamCursor or BufferedCursor class.
   
       :rtype: int
       :return: Number of documents in the result set.
       """
       result_counter = 0
       for row in cursor:
           # Each row would be in Ion format.
           print_ion(row)
           result_counter += 1
       return result_counter
   
   
   def print_ion(ion_value):
       """
       Pretty print an Ion Value.
   
       :type ion_value: :py:class:`amazon.ion.simple_types.IonPySymbol`
       :param ion_value: Any Ion Value to be pretty printed.
       """
       logger.info(dumps(ion_value, binary=False, indent='  ', omit_version_marker=True))
   ```
**Nota**  
La `get_document_ids` funzione esegue una query che restituisce un documento IDs assegnato dal sistema da una tabella. Per ulteriori informazioni, consulta [Utilizzo della clausola BY per interrogare l'ID del documento](working.metadata.by-clause.md).

1. Utilizzate il seguente programma (`insert_document.py`) per inserire i dati di esempio nelle tabelle.

------
#### [ 3.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.model.sample_data import convert_object_to_ion, SampleData, get_document_ids_from_dml_results
   from pyqldbsamples.connect_to_ledger import create_qldb_driver
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def update_person_id(document_ids):
       """
       Update the PersonId value for DriversLicense records and the PrimaryOwner value for VehicleRegistration records.
   
       :type document_ids: list
       :param document_ids: List of document IDs.
   
       :rtype: list
       :return: Lists of updated DriversLicense records and updated VehicleRegistration records.
       """
       new_drivers_licenses = SampleData.DRIVERS_LICENSE.copy()
       new_vehicle_registrations = SampleData.VEHICLE_REGISTRATION.copy()
       for i in range(len(SampleData.PERSON)):
           drivers_license = new_drivers_licenses[i]
           registration = new_vehicle_registrations[i]
           drivers_license.update({'PersonId': str(document_ids[i])})
           registration['Owners']['PrimaryOwner'].update({'PersonId': str(document_ids[i])})
       return new_drivers_licenses, new_vehicle_registrations
   
   
   def insert_documents(driver, table_name, documents):
       """
       Insert the given list of documents into a table in a single transaction.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type table_name: str
       :param table_name: Name of the table to insert documents into.
   
       :type documents: list
       :param documents: List of documents to insert.
   
       :rtype: list
       :return: List of documents IDs for the newly inserted documents.
       """
       logger.info('Inserting some documents in the {} table...'.format(table_name))
       statement = 'INSERT INTO {} ?'.format(table_name)
       cursor = driver.execute_lambda(lambda executor: executor.execute_statement(statement,
                                                                                  convert_object_to_ion(documents)))
       list_of_document_ids = get_document_ids_from_dml_results(cursor)
   
       return list_of_document_ids
   
   
   def update_and_insert_documents(driver):
       """
       Handle the insertion of documents and updating PersonIds.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
       """
       list_ids = insert_documents(driver, Constants.PERSON_TABLE_NAME, SampleData.PERSON)
   
       logger.info("Updating PersonIds for 'DriversLicense' and PrimaryOwner for 'VehicleRegistration'...")
       new_licenses, new_registrations = update_person_id(list_ids)
   
       insert_documents(driver, Constants.VEHICLE_TABLE_NAME, SampleData.VEHICLE)
       insert_documents(driver, Constants.VEHICLE_REGISTRATION_TABLE_NAME, new_registrations)
       insert_documents(driver, Constants.DRIVERS_LICENSE_TABLE_NAME, new_licenses)
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Insert documents into a table in a QLDB ledger.
       """
       try:
           with create_qldb_driver(ledger_name) as driver:
               # An INSERT statement creates the initial revision of a document with a version number of zero.
               # QLDB also assigns a unique document identifier in GUID format as part of the metadata.
               update_and_insert_documents(driver)
               logger.info('Documents inserted successfully!')
       except Exception as e:
           logger.exception('Error inserting or updating documents.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```

------
#### [ 2.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.model.sample_data import convert_object_to_ion, SampleData, get_document_ids_from_dml_results
   from pyqldbsamples.connect_to_ledger import create_qldb_session
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def update_person_id(document_ids):
       """
       Update the PersonId value for DriversLicense records and the PrimaryOwner value for VehicleRegistration records.
   
       :type document_ids: list
       :param document_ids: List of document IDs.
   
       :rtype: list
       :return: Lists of updated DriversLicense records and updated VehicleRegistration records.
       """
       new_drivers_licenses = SampleData.DRIVERS_LICENSE.copy()
       new_vehicle_registrations = SampleData.VEHICLE_REGISTRATION.copy()
       for i in range(len(SampleData.PERSON)):
           drivers_license = new_drivers_licenses[i]
           registration = new_vehicle_registrations[i]
           drivers_license.update({'PersonId': str(document_ids[i])})
           registration['Owners']['PrimaryOwner'].update({'PersonId': str(document_ids[i])})
       return new_drivers_licenses, new_vehicle_registrations
   
   
   def insert_documents(transaction_executor, table_name, documents):
       """
       Insert the given list of documents into a table in a single transaction.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type table_name: str
       :param table_name: Name of the table to insert documents into.
   
       :type documents: list
       :param documents: List of documents to insert.
   
       :rtype: list
       :return: List of documents IDs for the newly inserted documents.
       """
       logger.info('Inserting some documents in the {} table...'.format(table_name))
       statement = 'INSERT INTO {} ?'.format(table_name)
       cursor = transaction_executor.execute_statement(statement, convert_object_to_ion(documents))
       list_of_document_ids = get_document_ids_from_dml_results(cursor)
   
       return list_of_document_ids
   
   
   def update_and_insert_documents(transaction_executor):
       """
       Handle the insertion of documents and updating PersonIds all in a single transaction.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
       """
       list_ids = insert_documents(transaction_executor, Constants.PERSON_TABLE_NAME, SampleData.PERSON)
   
       logger.info("Updating PersonIds for 'DriversLicense' and PrimaryOwner for 'VehicleRegistration'...")
       new_licenses, new_registrations = update_person_id(list_ids)
   
       insert_documents(transaction_executor, Constants.VEHICLE_TABLE_NAME, SampleData.VEHICLE)
       insert_documents(transaction_executor, Constants.VEHICLE_REGISTRATION_TABLE_NAME, new_registrations)
       insert_documents(transaction_executor, Constants.DRIVERS_LICENSE_TABLE_NAME, new_licenses)
   
   
   if __name__ == '__main__':
       """
       Insert documents into a table in a QLDB ledger.
       """
       try:
           with create_qldb_session() as session:
               # An INSERT statement creates the initial revision of a document with a version number of zero.
               # QLDB also assigns a unique document identifier in GUID format as part of the metadata.
               session.execute_lambda(lambda executor: update_and_insert_documents(executor),
                                      lambda retry_attempt: logger.info('Retrying due to OCC conflict...'))
               logger.info('Documents inserted successfully!')
       except Exception:
           logger.exception('Error inserting or updating documents.')
   ```

------
**Nota**  
Questo programma dimostra come chiamare la `execute_statement` funzione con valori parametrizzati. È possibile passare parametri di dati oltre all'istruzione PartiQL che si desidera eseguire. Utilizzate un punto interrogativo (`?`) come segnaposto variabile nella stringa dell'istruzione.
Se un'`INSERT`istruzione ha esito positivo, restituisce il valore `id` di ogni documento inserito.

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python insert_document.py
   ```

Successivamente, è possibile utilizzare `SELECT` le istruzioni per leggere i dati dalle tabelle del `vehicle-registration` libro mastro. Passa a [Passaggio 4: interrogare le tabelle in un libro mastro](getting-started.python.step-4.md).

# Passaggio 4: interrogare le tabelle in un libro mastro
Fase 4: Interrogare le tabelle

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Dopo aver creato tabelle in un registro Amazon QLDB e averle caricate con i dati, puoi eseguire query per rivedere i dati di immatricolazione dei veicoli che hai appena inserito. QLDB [utilizza](ql-reference.md) PartiQL come linguaggio di interrogazione [e Amazon](ion.md) Ion come modello di dati orientato ai documenti.

PartiQL è un linguaggio di query open source compatibile con SQL che è stato esteso per funzionare con Ion. Con PartiQL, puoi inserire, interrogare e gestire i tuoi dati con operatori SQL familiari. Amazon Ion è un superset di JSON. Ion è un formato di dati open source basato su documenti che offre la flessibilità di archiviazione ed elaborazione di dati strutturati, semistrutturati e annidati.

In questo passaggio, si utilizzano `SELECT` le istruzioni per leggere i dati dalle tabelle del registro. `vehicle-registration`

**avvertimento**  
Quando si esegue una query in QLDB senza una ricerca indicizzata, viene richiamata una scansione completa della tabella. PartiQL supporta tali query perché è compatibile con SQL. Tuttavia, *non* eseguire scansioni di tabelle per casi d'uso di produzione in QLDB. Le scansioni delle tabelle possono causare problemi di prestazioni su tabelle di grandi dimensioni, inclusi conflitti di concorrenza e timeout delle transazioni.  
Per evitare le scansioni delle tabelle, è necessario 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)` Per ulteriori informazioni, consulta [Ottimizzazione delle prestazioni delle query](working.optimize.md).

**Per interrogare le tabelle**

1. Usa il seguente programma (`find_vehicles.py`) per interrogare tutti i veicoli registrati con una persona nel tuo registro.

------
#### [ 3.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.model.sample_data import get_document_ids, print_result, SampleData
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_driver
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def find_vehicles_for_owner(driver, gov_id):
       """
       Find vehicles registered under a driver using their government ID.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type gov_id: str
       :param gov_id: The owner's government ID.
       """
       document_ids = driver.execute_lambda(lambda executor: get_document_ids(executor, Constants.PERSON_TABLE_NAME,
                                                                              'GovId', gov_id))
   
       query = "SELECT Vehicle FROM Vehicle INNER JOIN VehicleRegistration AS r " \
               "ON Vehicle.VIN = r.VIN WHERE r.Owners.PrimaryOwner.PersonId = ?"
   
       for ids in document_ids:
           cursor = driver.execute_lambda(lambda executor: executor.execute_statement(query, ids))
           logger.info('List of Vehicles for owner with GovId: {}...'.format(gov_id))
           print_result(cursor)
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Find all vehicles registered under a person.
       """
       try:
           with create_qldb_driver(ledger_name) as driver:
               # Find all vehicles registered under a person.
               gov_id = SampleData.PERSON[0]['GovId']
               find_vehicles_for_owner(driver, gov_id)
       except Exception as e:
           logger.exception('Error getting vehicles for owner.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```

------
#### [ 2.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.model.sample_data import get_document_ids, print_result, SampleData
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_session
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def find_vehicles_for_owner(transaction_executor, gov_id):
       """
       Find vehicles registered under a driver using their government ID.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type gov_id: str
       :param gov_id: The owner's government ID.
       """
       document_ids = get_document_ids(transaction_executor, Constants.PERSON_TABLE_NAME, 'GovId', gov_id)
   
       query = "SELECT Vehicle FROM Vehicle INNER JOIN VehicleRegistration AS r " \
               "ON Vehicle.VIN = r.VIN WHERE r.Owners.PrimaryOwner.PersonId = ?"
   
       for ids in document_ids:
           cursor = transaction_executor.execute_statement(query, ids)
           logger.info('List of Vehicles for owner with GovId: {}...'.format(gov_id))
           print_result(cursor)
   
   
   if __name__ == '__main__':
       """
       Find all vehicles registered under a person.
       """
       try:
           with create_qldb_session() as session:
               # Find all vehicles registered under a person.
               gov_id = SampleData.PERSON[0]['GovId']
               session.execute_lambda(lambda executor: find_vehicles_for_owner(executor, gov_id),
                                      lambda retry_attempt: logger.info('Retrying due to OCC conflict...'))
       except Exception:
           logger.exception('Error getting vehicles for owner.')
   ```

------
**Nota**  
Innanzitutto, questo programma interroga la `Person` tabella del documento per ottenere il relativo `GovId LEWISR261LL` campo di `id` metadati.  
Quindi, utilizza questo documento `id` come chiave esterna per interrogare la `VehicleRegistration` tabella. `PrimaryOwner.PersonId` Inoltre si unisce `VehicleRegistration` alla `Vehicle` tabella sul `VIN` campo.

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python find_vehicles.py
   ```

Per ulteriori informazioni sulla modifica dei documenti nelle tabelle del `vehicle-registration` libro mastro, consulta. [Fase 5: Modificare i documenti in un libro mastro](getting-started.python.step-5.md)

# Fase 5: Modificare i documenti in un libro mastro
Fase 5: Modificare i documenti

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Ora che hai dei dati su cui lavorare, puoi iniziare a modificare i documenti nel `vehicle-registration` registro di Amazon QLDB. In questa fase, i seguenti esempi di codice mostrano come eseguire istruzioni DML (Data Manipulation Language). Queste istruzioni aggiornano il proprietario principale di un veicolo e aggiungono un proprietario secondario a un altro veicolo.

**Per modificare i documenti**

1. Utilizzate il seguente programma (`transfer_vehicle_ownership.py`) per aggiornare il proprietario principale del veicolo inserendo il `1N4AL11D75C109151` VIN nel registro.

------
#### [ 3.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.add_secondary_owner import get_document_ids, print_result, SampleData
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.model.sample_data import convert_object_to_ion
   from pyqldbsamples.connect_to_ledger import create_qldb_driver
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def find_person_from_document_id(transaction_executor, document_id):
       """
       Query a driver's information using the given ID.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type document_id: :py:class:`amazon.ion.simple_types.IonPyText`
       :param document_id: The document ID required to query for the person.
   
       :rtype: :py:class:`amazon.ion.simple_types.IonPyDict`
       :return: The resulting document from the query.
       """
       query = 'SELECT p.* FROM Person AS p BY pid WHERE pid = ?'
       cursor = transaction_executor.execute_statement(query, document_id)
       return next(cursor)
   
   
   def find_primary_owner_for_vehicle(driver, vin):
       """
       Find the primary owner of a vehicle given its VIN.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type vin: str
       :param vin: The VIN to find primary owner for.
   
       :rtype: :py:class:`amazon.ion.simple_types.IonPyDict`
       :return: The resulting document from the query.
       """
       logger.info('Finding primary owner for vehicle with VIN: {}.'.format(vin))
       query = "SELECT Owners.PrimaryOwner.PersonId FROM VehicleRegistration AS v WHERE v.VIN = ?"
       cursor = driver.execute_lambda(lambda executor: executor.execute_statement(query, convert_object_to_ion(vin)))
       try:
           return driver.execute_lambda(lambda executor: find_person_from_document_id(executor,
                                                                                      next(cursor).get('PersonId')))
       except StopIteration:
           logger.error('No primary owner registered for this vehicle.')
           return None
   
   
   def update_vehicle_registration(driver, vin, document_id):
       """
       Update the primary owner for a vehicle using the given VIN.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type vin: str
       :param vin: The VIN for the vehicle to operate on.
   
       :type document_id: :py:class:`amazon.ion.simple_types.IonPyText`
       :param document_id: New PersonId for the primary owner.
   
       :raises RuntimeError: If no vehicle registration was found using the given document ID and VIN.
       """
       logger.info('Updating the primary owner for vehicle with Vin: {}...'.format(vin))
       statement = "UPDATE VehicleRegistration AS r SET r.Owners.PrimaryOwner.PersonId = ? WHERE r.VIN = ?"
       cursor = driver.execute_lambda(lambda executor: executor.execute_statement(statement, document_id,
                                                                                  convert_object_to_ion(vin)))
       try:
           print_result(cursor)
           logger.info('Successfully transferred vehicle with VIN: {} to new owner.'.format(vin))
       except StopIteration:
           raise RuntimeError('Unable to transfer vehicle, could not find registration.')
   
   
   def validate_and_update_registration(driver, vin, current_owner, new_owner):
       """
       Validate the current owner of the given vehicle and transfer its ownership to a new owner.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type vin: str
       :param vin: The VIN of the vehicle to transfer ownership of.
   
       :type current_owner: str
       :param current_owner: The GovId of the current owner of the vehicle.
   
       :type new_owner: str
       :param new_owner: The GovId of the new owner of the vehicle.
   
       :raises RuntimeError: If unable to verify primary owner.
       """
       primary_owner = find_primary_owner_for_vehicle(driver, vin)
       if primary_owner is None or primary_owner['GovId'] != current_owner:
           raise RuntimeError('Incorrect primary owner identified for vehicle, unable to transfer.')
   
       document_ids = driver.execute_lambda(lambda executor: get_document_ids(executor, Constants.PERSON_TABLE_NAME,
                                                                              'GovId', new_owner))
       update_vehicle_registration(driver, vin, document_ids[0])
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Find primary owner for a particular vehicle's VIN.
       Transfer to another primary owner for a particular vehicle's VIN.
       """
       vehicle_vin = SampleData.VEHICLE[0]['VIN']
       previous_owner = SampleData.PERSON[0]['GovId']
       new_owner = SampleData.PERSON[1]['GovId']
   
       try:
           with create_qldb_driver(ledger_name) as driver:
               validate_and_update_registration(driver, vehicle_vin, previous_owner, new_owner)
       except Exception as e:
           logger.exception('Error updating VehicleRegistration.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```

------
#### [ 2.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.add_secondary_owner import get_document_ids, print_result, SampleData
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.model.sample_data import convert_object_to_ion
   from pyqldbsamples.connect_to_ledger import create_qldb_session
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def find_person_from_document_id(transaction_executor, document_id):
       """
       Query a driver's information using the given ID.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type document_id: :py:class:`amazon.ion.simple_types.IonPyText`
       :param document_id: The document ID required to query for the person.
   
       :rtype: :py:class:`amazon.ion.simple_types.IonPyDict`
       :return: The resulting document from the query.
       """
       query = 'SELECT p.* FROM Person AS p BY pid WHERE pid = ?'
       cursor = transaction_executor.execute_statement(query, document_id)
       return next(cursor)
   
   
   def find_primary_owner_for_vehicle(transaction_executor, vin):
       """
       Find the primary owner of a vehicle given its VIN.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type vin: str
       :param vin: The VIN to find primary owner for.
   
       :rtype: :py:class:`amazon.ion.simple_types.IonPyDict`
       :return: The resulting document from the query.
       """
       logger.info('Finding primary owner for vehicle with VIN: {}.'.format(vin))
       query = "SELECT Owners.PrimaryOwner.PersonId FROM VehicleRegistration AS v WHERE v.VIN = ?"
       cursor = transaction_executor.execute_statement(query, convert_object_to_ion(vin))
       try:
           return find_person_from_document_id(transaction_executor, next(cursor).get('PersonId'))
       except StopIteration:
           logger.error('No primary owner registered for this vehicle.')
           return None
   
   
   def update_vehicle_registration(transaction_executor, vin, document_id):
       """
       Update the primary owner for a vehicle using the given VIN.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type vin: str
       :param vin: The VIN for the vehicle to operate on.
   
       :type document_id: :py:class:`amazon.ion.simple_types.IonPyText`
       :param document_id: New PersonId for the primary owner.
   
       :raises RuntimeError: If no vehicle registration was found using the given document ID and VIN.
       """
       logger.info('Updating the primary owner for vehicle with Vin: {}...'.format(vin))
       statement = "UPDATE VehicleRegistration AS r SET r.Owners.PrimaryOwner.PersonId = ? WHERE r.VIN = ?"
       cursor = transaction_executor.execute_statement(statement, document_id, convert_object_to_ion(vin))
       try:
           print_result(cursor)
           logger.info('Successfully transferred vehicle with VIN: {} to new owner.'.format(vin))
       except StopIteration:
           raise RuntimeError('Unable to transfer vehicle, could not find registration.')
   
   
   def validate_and_update_registration(transaction_executor, vin, current_owner, new_owner):
       """
       Validate the current owner of the given vehicle and transfer its ownership to a new owner in a single transaction.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type vin: str
       :param vin: The VIN of the vehicle to transfer ownership of.
   
       :type current_owner: str
       :param current_owner: The GovId of the current owner of the vehicle.
   
       :type new_owner: str
       :param new_owner: The GovId of the new owner of the vehicle.
   
       :raises RuntimeError: If unable to verify primary owner.
       """
       primary_owner = find_primary_owner_for_vehicle(transaction_executor, vin)
       if primary_owner is None or primary_owner['GovId'] != current_owner:
           raise RuntimeError('Incorrect primary owner identified for vehicle, unable to transfer.')
   
       document_id = next(get_document_ids(transaction_executor, Constants.PERSON_TABLE_NAME, 'GovId', new_owner))
   
       update_vehicle_registration(transaction_executor, vin, document_id)
   
   
   if __name__ == '__main__':
       """
       Find primary owner for a particular vehicle's VIN.
       Transfer to another primary owner for a particular vehicle's VIN.
       """
       vehicle_vin = SampleData.VEHICLE[0]['VIN']
       previous_owner = SampleData.PERSON[0]['GovId']
       new_owner = SampleData.PERSON[1]['GovId']
   
       try:
           with create_qldb_session() as session:
               session.execute_lambda(lambda executor: validate_and_update_registration(executor, vehicle_vin,
                                                                                        previous_owner, new_owner),
                                      retry_indicator=lambda retry_attempt: logger.info('Retrying due to OCC conflict...'))
       except Exception:
           logger.exception('Error updating VehicleRegistration.')
   ```

------

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python transfer_vehicle_ownership.py
   ```

1. Utilizza il seguente programma (`add_secondary_owner.py`) per aggiungere un proprietario secondario al veicolo con VIN `KM8SRDHF6EU074761` nel registro.

------
#### [ 3.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.model.sample_data import to_ion_struct, get_document_ids, print_result, SampleData, \
       convert_object_to_ion
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_driver
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def get_document_id_by_gov_id(driver, government_id):
       """
       Find a driver's person ID using the given government ID.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type government_id: str
       :param government_id: A driver's government ID.
   
       :rtype: list
       :return: A list of document IDs.
       """
       logger.info("Finding secondary owner's person ID using given government ID: {}.".format(government_id))
       return driver.execute_lambda(lambda executor: get_document_ids(executor, Constants.PERSON_TABLE_NAME, 'GovId',
                                                                      government_id))
   
   
   def is_secondary_owner_for_vehicle(driver, vin, secondary_owner_id):
       """
       Check whether a secondary owner has already been registered for the given VIN.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type vin: str
       :param vin: VIN of the vehicle to query.
   
       :type secondary_owner_id: str
       :param secondary_owner_id: The secondary owner's person ID.
   
       :rtype: bool
       :return: If the driver has already been registered.
       """
       logger.info('Finding secondary owners for vehicle with VIN: {}...'.format(vin))
       query = 'SELECT Owners.SecondaryOwners FROM VehicleRegistration AS v WHERE v.VIN = ?'
       rows = driver.execute_lambda(lambda executor: executor.execute_statement(query, convert_object_to_ion(vin)))
   
       for row in rows:
           secondary_owners = row.get('SecondaryOwners')
           person_ids = map(lambda owner: owner.get('PersonId').text, secondary_owners)
           if secondary_owner_id in person_ids:
               return True
       return False
   
   
   def add_secondary_owner_for_vin(driver, vin, parameter):
       """
       Add a secondary owner into `VehicleRegistration` table for a particular VIN.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type vin: str
       :param vin: VIN of the vehicle to add a secondary owner for.
   
       :type parameter: :py:class:`amazon.ion.simple_types.IonPyValue`
       :param parameter: The Ion value or Python native type that is convertible to Ion for filling in parameters of the
                         statement.
       """
       logger.info('Inserting secondary owner for vehicle with VIN: {}...'.format(vin))
       statement = "FROM VehicleRegistration AS v WHERE v.VIN = ? INSERT INTO v.Owners.SecondaryOwners VALUE ?"
   
       cursor = driver.execute_lambda(lambda executor: executor.execute_statement(statement, convert_object_to_ion(vin),
                                                                                  parameter))
       logger.info('VehicleRegistration Document IDs which had secondary owners added: ')
       print_result(cursor)
   
   
   def register_secondary_owner(driver, vin, gov_id):
       """
       Register a secondary owner for a vehicle if they are not already registered.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type vin: str
       :param vin: VIN of the vehicle to register a secondary owner for.
   
       :type gov_id: str
       :param gov_id: The government ID of the owner.
       """
       logger.info('Finding the secondary owners for vehicle with VIN: {}.'.format(vin))
   
       document_ids = get_document_id_by_gov_id(driver, gov_id)
   
       for document_id in document_ids:
           if is_secondary_owner_for_vehicle(driver, vin, document_id):
               logger.info('Person with ID {} has already been added as a secondary owner of this vehicle.'.format(gov_id))
           else:
               add_secondary_owner_for_vin(driver, vin, to_ion_struct('PersonId', document_id))
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Finds and adds secondary owners for a vehicle.
       """
       vin = SampleData.VEHICLE[1]['VIN']
       gov_id = SampleData.PERSON[0]['GovId']
       try:
           with create_qldb_driver(ledger_name) as driver:
               register_secondary_owner(driver, vin, gov_id)
               logger.info('Secondary owners successfully updated.')
       except Exception as e:
           logger.exception('Error adding secondary owner.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```

------
#### [ 2.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.model.sample_data import to_ion_struct, get_document_ids, print_result, SampleData, \
       convert_object_to_ion
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_session
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def get_document_id_by_gov_id(transaction_executor, government_id):
       """
       Find a driver's person ID using the given government ID.
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
       :type government_id: str
       :param government_id: A driver's government ID.
       :rtype: list
       :return: A list of document IDs.
       """
       logger.info("Finding secondary owner's person ID using given government ID: {}.".format(government_id))
       return get_document_ids(transaction_executor, Constants.PERSON_TABLE_NAME, 'GovId', government_id)
   
   
   def is_secondary_owner_for_vehicle(transaction_executor, vin, secondary_owner_id):
       """
       Check whether a secondary owner has already been registered for the given VIN.
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
       :type vin: str
       :param vin: VIN of the vehicle to query.
       :type secondary_owner_id: str
       :param secondary_owner_id: The secondary owner's person ID.
       :rtype: bool
       :return: If the driver has already been registered.
       """
       logger.info('Finding secondary owners for vehicle with VIN: {}...'.format(vin))
       query = 'SELECT Owners.SecondaryOwners FROM VehicleRegistration AS v WHERE v.VIN = ?'
       rows = transaction_executor.execute_statement(query, convert_object_to_ion(vin))
   
       for row in rows:
           secondary_owners = row.get('SecondaryOwners')
           person_ids = map(lambda owner: owner.get('PersonId').text, secondary_owners)
           if secondary_owner_id in person_ids:
               return True
       return False
   
   
   def add_secondary_owner_for_vin(transaction_executor, vin, parameter):
       """
       Add a secondary owner into `VehicleRegistration` table for a particular VIN.
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
       :type vin: str
       :param vin: VIN of the vehicle to add a secondary owner for.
       :type parameter: :py:class:`amazon.ion.simple_types.IonPyValue`
       :param parameter: The Ion value or Python native type that is convertible to Ion for filling in parameters of the
                         statement.
       """
       logger.info('Inserting secondary owner for vehicle with VIN: {}...'.format(vin))
       statement = "FROM VehicleRegistration AS v WHERE v.VIN = '{}' INSERT INTO v.Owners.SecondaryOwners VALUE ?"\
           .format(vin)
   
       cursor = transaction_executor.execute_statement(statement, parameter)
       logger.info('VehicleRegistration Document IDs which had secondary owners added: ')
       print_result(cursor)
   
   
   def register_secondary_owner(transaction_executor, vin, gov_id):
       """
       Register a secondary owner for a vehicle if they are not already registered.
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
       :type vin: str
       :param vin: VIN of the vehicle to register a secondary owner for.
       :type gov_id: str
       :param gov_id: The government ID of the owner.
       """
       logger.info('Finding the secondary owners for vehicle with VIN: {}.'.format(vin))
       document_ids = get_document_id_by_gov_id(transaction_executor, gov_id)
   
       for document_id in document_ids:
           if is_secondary_owner_for_vehicle(transaction_executor, vin, document_id):
               logger.info('Person with ID {} has already been added as a secondary owner of this vehicle.'.format(gov_id))
           else:
               add_secondary_owner_for_vin(transaction_executor, vin, to_ion_struct('PersonId', document_id))
   
   
   if __name__ == '__main__':
       """
       Finds and adds secondary owners for a vehicle.
       """
       vin = SampleData.VEHICLE[1]['VIN']
       gov_id = SampleData.PERSON[0]['GovId']
       try:
           with create_qldb_session() as session:
               session.execute_lambda(lambda executor: register_secondary_owner(executor, vin, gov_id),
                                      lambda retry_attempt: logger.info('Retrying due to OCC conflict...'))
               logger.info('Secondary owners successfully updated.')
       except Exception:
           logger.exception('Error adding secondary owner.')
   ```

------

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python add_secondary_owner.py
   ```

Per esaminare queste modifiche nel `vehicle-registration` libro mastro, consulta. [Fase 6: Visualizzare la cronologia delle revisioni di un documento](getting-started.python.step-6.md)

# Fase 6: Visualizzare la cronologia delle revisioni di un documento
Passaggio 6: Visualizza la cronologia delle revisioni

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Dopo aver modificato i dati di immatricolazione di un veicolo nel passaggio precedente, puoi interrogare la cronologia di tutti i proprietari registrati e qualsiasi altro campo aggiornato. In questo passaggio, esegui una query sulla cronologia delle revisioni di un documento nella `VehicleRegistration` tabella del registro. `vehicle-registration`

**Per visualizzare la cronologia delle revisioni**

1. Utilizzate il seguente programma (`query_history.py`) per interrogare la cronologia delle revisioni del `VehicleRegistration` documento con VIN. `1N4AL11D75C109151`

------
#### [ 3.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from datetime import datetime, timedelta
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.model.sample_data import print_result, get_document_ids, SampleData
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_driver
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def format_date_time(date_time):
       """
       Format the given date time to a string.
   
       :type date_time: :py:class:`datetime.datetime`
       :param date_time: The date time to format.
   
       :rtype: str
       :return: The formatted date time.
       """
       return date_time.strftime('`%Y-%m-%dT%H:%M:%S.%fZ`')
   
   
   def previous_primary_owners(driver, vin):
       """
       Find previous primary owners for the given VIN in a single transaction.
       In this example, query the `VehicleRegistration` history table to find all previous primary owners for a VIN.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type vin: str
       :param vin: VIN to find previous primary owners for.
       """
       person_ids = driver.execute_lambda(lambda executor: get_document_ids(executor,
                                                                            Constants.VEHICLE_REGISTRATION_TABLE_NAME,
                                                                            'VIN', vin))
   
       todays_date = datetime.utcnow() - timedelta(seconds=1)
       three_months_ago = todays_date - timedelta(days=90)
       query = 'SELECT data.Owners.PrimaryOwner, metadata.version FROM history({}, {}, {}) AS h WHERE h.metadata.id = ?'.\
           format(Constants.VEHICLE_REGISTRATION_TABLE_NAME, format_date_time(three_months_ago),
                  format_date_time(todays_date))
   
       for ids in person_ids:
           logger.info("Querying the 'VehicleRegistration' table's history using VIN: {}.".format(vin))
           cursor = driver.execute_lambda(lambda executor: executor.execute_statement(query, ids))
           if not (print_result(cursor)) > 0:
               logger.info('No modification history found within the given time frame for document ID: {}'.format(ids))
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Query a table's history for a particular set of documents.
       """
       try:
           with create_qldb_driver(ledger_name) as driver:
               vin = SampleData.VEHICLE_REGISTRATION[0]['VIN']
               previous_primary_owners(driver, vin)
               logger.info('Successfully queried history.')
       except Exception as e:
           logger.exception('Unable to query history to find previous owners.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```

------
#### [ 2.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from datetime import datetime, timedelta
   from logging import basicConfig, getLogger, INFO
   
   from pyqldbsamples.model.sample_data import print_result, get_document_ids, SampleData
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.connect_to_ledger import create_qldb_session
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   
   
   def format_date_time(date_time):
       """
       Format the given date time to a string.
   
       :type date_time: :py:class:`datetime.datetime`
       :param date_time: The date time to format.
   
       :rtype: str
       :return: The formatted date time.
       """
       return date_time.strftime('`%Y-%m-%dT%H:%M:%S.%fZ`')
   
   
   def previous_primary_owners(transaction_executor, vin):
       """
       Find previous primary owners for the given VIN in a single transaction.
       In this example, query the `VehicleRegistration` history table to find all previous primary owners for a VIN.
   
       :type transaction_executor: :py:class:`pyqldb.execution.executor.Executor`
       :param transaction_executor: An Executor object allowing for execution of statements within a transaction.
   
       :type vin: str
       :param vin: VIN to find previous primary owners for.
       """
       person_ids = get_document_ids(transaction_executor, Constants.VEHICLE_REGISTRATION_TABLE_NAME, 'VIN', vin)
   
       todays_date = datetime.utcnow() - timedelta(seconds=1)
       three_months_ago = todays_date - timedelta(days=90)
       query = 'SELECT data.Owners.PrimaryOwner, metadata.version FROM history({}, {}, {}) AS h WHERE h.metadata.id = ?'.\
           format(Constants.VEHICLE_REGISTRATION_TABLE_NAME, format_date_time(three_months_ago),
                  format_date_time(todays_date))
   
       for ids in person_ids:
           logger.info("Querying the 'VehicleRegistration' table's history using VIN: {}.".format(vin))
           cursor = transaction_executor.execute_statement(query, ids)
           if not (print_result(cursor)) > 0:
               logger.info('No modification history found within the given time frame for document ID: {}'.format(ids))
   
   
   if __name__ == '__main__':
       """
       Query a table's history for a particular set of documents.
       """
       try:
           with create_qldb_session() as session:
               vin = SampleData.VEHICLE_REGISTRATION[0]['VIN']
               session.execute_lambda(lambda lambda_executor: previous_primary_owners(lambda_executor, vin),
                                      lambda retry_attempt: logger.info('Retrying due to OCC conflict...'))
               logger.info('Successfully queried history.')
       except Exception:
           logger.exception('Unable to query history to find previous owners.')
   ```

------
**Nota**  
È possibile visualizzare la cronologia delle revisioni di un documento interrogando la sintassi incorporata [Funzione di cronologia](working.history.md#working.history.function) nella seguente sintassi.  

     ```
     SELECT * FROM history( table_name [, `start-time` [, `end-time` ] ] ) AS h
     [ WHERE h.metadata.id = 'id' ]
     ```
L'*ora di inizio* e l'ora di *fine* sono entrambe opzionali. Sono valori letterali di Amazon Ion che possono essere indicati con backticks (). ``...`` Per ulteriori informazioni, consulta [Interrogazione di Ion con PartiQL in Amazon QLDB](ql-reference.query.md).
Come best practice, qualifica una query cronologica con un intervallo di date (ora di *inizio e ora di fine) e un* *ID del documento ()*. `metadata.id` [QLDB `SELECT` elabora le query nelle transazioni, che sono soggette a un limite di timeout delle transazioni.](limits.md#limits.fixed)  
La cronologia QLDB è indicizzata in base all'ID del documento e al momento non è possibile creare indici di cronologia aggiuntivi. Le interrogazioni cronologiche che includono l'ora di inizio e l'ora di fine ottengono il vantaggio della qualificazione per intervalli di date.

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python query_history.py
   ```

Per verificare crittograficamente una revisione di un documento nel registro, procedi a. `vehicle-registration` [Fase 7: Verificare un documento in un libro mastro](getting-started.python.step-7.md)

# Fase 7: Verificare un documento in un libro mastro
Fase 7: Verifica di un documento

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Con Amazon QLDB, puoi verificare in modo efficiente l'integrità di un documento nel diario del tuo libro mastro utilizzando l'hashing crittografico con SHA-256. Per ulteriori informazioni su come funzionano la verifica e l'hashing crittografico in QLDB, consulta. [Verifica dei dati in Amazon QLDB](verification.md)

In questo passaggio, verifichi la revisione di un documento nella tabella del `VehicleRegistration` registro. `vehicle-registration` Innanzitutto, richiedi un digest, che viene restituito come file di output e funge da firma dell'intera cronologia delle modifiche del registro. Quindi, richiedi una bozza della revisione relativa a quel digest. Utilizzando questa prova, l'integrità della revisione viene verificata se tutti i controlli di convalida vengono superati.

**Per verificare la revisione di un documento**

1. Esamina i seguenti `.py` file, che rappresentano oggetti QLDB necessari per la verifica e un modulo di utilità con funzioni di supporto per convertire i tipi di risposta QLDB in stringhe.

   1. `block_address.py`

      ```
      # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
      # SPDX-License-Identifier: MIT-0
      #
      # Permission is hereby granted, free of charge, to any person obtaining a copy of this
      # software and associated documentation files (the "Software"), to deal in the Software
      # without restriction, including without limitation the rights to use, copy, modify,
      # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
      # permit persons to whom the Software is furnished to do so.
      #
      # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
      # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
      # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      
      
      def block_address_to_dictionary(ion_dict):
          """
          Convert a block address from IonPyDict into a dictionary.
          Shape of the dictionary must be: {'IonText': "{strandId: <"strandId">, sequenceNo: <sequenceNo>}"}
      
          :type ion_dict: :py:class:`amazon.ion.simple_types.IonPyDict`/str
          :param ion_dict: The block address value to convert.
      
          :rtype: dict
          :return: The converted dict.
          """
          block_address = {'IonText': {}}
          if not isinstance(ion_dict, str):
              py_dict = '{{strandId: "{}", sequenceNo:{}}}'.format(ion_dict['strandId'], ion_dict['sequenceNo'])
              ion_dict = py_dict
          block_address['IonText'] = ion_dict
          return block_address
      ```

   1. `verifier.py`

      ```
      # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
      # SPDX-License-Identifier: MIT-0
      #
      # Permission is hereby granted, free of charge, to any person obtaining a copy of this
      # software and associated documentation files (the "Software"), to deal in the Software
      # without restriction, including without limitation the rights to use, copy, modify,
      # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
      # permit persons to whom the Software is furnished to do so.
      #
      # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
      # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
      # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      #
      # This code expects that you have AWS credentials setup per:
      # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
      from array import array
      from base64 import b64encode
      from functools import reduce
      from hashlib import sha256
      from random import randrange
      
      from amazon.ion.simpleion import loads
      
      HASH_LENGTH = 32
      UPPER_BOUND = 8
      
      
      def parse_proof(value_holder):
          """
          Parse the Proof object returned by QLDB into an iterator.
      
          The Proof object returned by QLDB is a dictionary like the following:
          {'IonText': '[{{<hash>}},{{<hash>}}]'}
      
          :type value_holder: dict
          :param value_holder: A structure containing an Ion string value.
      
          :rtype: :py:class:`amazon.ion.simple_types.IonPyList`
          :return: A list of hash values.
          """
          value_holder = value_holder.get('IonText')
          proof_list = loads(value_holder)
          return proof_list
      
      
      def parse_block(value_holder):
          """
          Parse the Block object returned by QLDB and retrieve block hash.
      
          :type value_holder: dict
          :param value_holder: A structure containing an Ion string value.
      
          :rtype: :py:class:`amazon.ion.simple_types.IonPyBytes`
          :return: The block hash.
          """
          value_holder = value_holder.get('IonText')
          block = loads(value_holder)
          block_hash = block.get('blockHash')
          return block_hash
      
      
      def flip_random_bit(original):
          """
          Flip a single random bit in the given hash value.
          This method is used to demonstrate QLDB's verification features.
      
          :type original: bytes
          :param original: The hash value to alter.
      
          :rtype: bytes
          :return: The altered hash with a single random bit changed.
          """
          assert len(original) != 0, 'Invalid bytes.'
      
          altered_position = randrange(len(original))
          bit_shift = randrange(UPPER_BOUND)
          altered_hash = bytearray(original).copy()
      
          altered_hash[altered_position] = altered_hash[altered_position] ^ (1 << bit_shift)
          return bytes(altered_hash)
      
      
      def compare_hash_values(hash1, hash2):
          """
          Compare two hash values by converting them into byte arrays, assuming they are little endian.
      
          :type hash1: bytes
          :param hash1: The hash value to compare.
      
          :type hash2: bytes
          :param hash2: The hash value to compare.
      
          :rtype: int
          :return: Zero if the hash values are equal, otherwise return the difference of the first pair of non-matching bytes.
          """
          assert len(hash1) == HASH_LENGTH
          assert len(hash2) == HASH_LENGTH
      
          hash_array1 = array('b', hash1)
          hash_array2 = array('b', hash2)
      
          for i in range(len(hash_array1) - 1, -1, -1):
              difference = hash_array1[i] - hash_array2[i]
              if difference != 0:
                  return difference
          return 0
      
      
      def join_hash_pairwise(hash1, hash2):
          """
          Take two hash values, sort them, concatenate them, and generate a new hash value from the concatenated values.
      
          :type hash1: bytes
          :param hash1: Hash value to concatenate.
      
          :type hash2: bytes
          :param hash2: Hash value to concatenate.
      
          :rtype: bytes
          :return: The new hash value generated from concatenated hash values.
          """
          if len(hash1) == 0:
              return hash2
          if len(hash2) == 0:
              return hash1
      
          concatenated = hash1 + hash2 if compare_hash_values(hash1, hash2) < 0 else hash2 + hash1
          new_hash_lib = sha256()
          new_hash_lib.update(concatenated)
          new_digest = new_hash_lib.digest()
          return new_digest
      
      
      def calculate_root_hash_from_internal_hashes(internal_hashes, leaf_hash):
          """
          Combine the internal hashes and the leaf hash until only one root hash remains.
      
          :type internal_hashes: map
          :param internal_hashes: An iterable over a list of hash values.
      
          :type leaf_hash: bytes
          :param leaf_hash: The revision hash to pair with the first hash in the Proof hashes list.
      
          :rtype: bytes
          :return: The root hash constructed by combining internal hashes.
          """
          root_hash = reduce(join_hash_pairwise, internal_hashes, leaf_hash)
          return root_hash
      
      
      def build_candidate_digest(proof, leaf_hash):
          """
          Build the candidate digest representing the entire ledger from the Proof hashes.
      
          :type proof: dict
          :param proof: The Proof object.
      
          :type leaf_hash: bytes
          :param leaf_hash: The revision hash to pair with the first hash in the Proof hashes list.
      
          :rtype: bytes
          :return: The calculated root hash.
          """
          parsed_proof = parse_proof(proof)
          root_hash = calculate_root_hash_from_internal_hashes(parsed_proof, leaf_hash)
          return root_hash
      
      
      def verify_document(document_hash, digest, proof):
          """
          Verify document revision against the provided digest.
      
          :type document_hash: bytes
          :param document_hash: The SHA-256 value representing the document revision to be verified.
      
          :type digest: bytes
          :param digest: The SHA-256 hash value representing the ledger digest.
      
          :type proof: dict
          :param proof: The Proof object retrieved from :func:`pyqldbsamples.get_revision.get_revision`.
      
          :rtype: bool
          :return: If the document revision verify against the ledger digest.
          """
          candidate_digest = build_candidate_digest(proof, document_hash)
          return digest == candidate_digest
      
      
      def to_base_64(input):
          """
          Encode input in base64.
      
          :type input: bytes
          :param input: Input to be encoded.
      
          :rtype: string
          :return: Return input that has been encoded in base64.
          """
          encoded_value = b64encode(input)
          return str(encoded_value, 'UTF-8')
      ```

   1. `qldb_string_utils.py`

      ```
      # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
      # SPDX-License-Identifier: MIT-0
      #
      # Permission is hereby granted, free of charge, to any person obtaining a copy of this
      # software and associated documentation files (the "Software"), to deal in the Software
      # without restriction, including without limitation the rights to use, copy, modify,
      # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
      # permit persons to whom the Software is furnished to do so.
      #
      # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
      # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
      # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      from amazon.ion.simpleion import dumps, loads
      
      
      def value_holder_to_string(value_holder):
          """
          Returns the string representation of a given `value_holder`.
      
          :type value_holder: dict
          :param value_holder: The `value_holder` to convert to string.
      
          :rtype: str
          :return: The string representation of the supplied `value_holder`.
          """
          ret_val = dumps(loads(value_holder), binary=False, indent='  ', omit_version_marker=True)
          val = '{{ IonText: {}}}'.format(ret_val)
          return val
      
      
      def block_response_to_string(block_response):
          """
          Returns the string representation of a given `block_response`.
      
          :type block_response: dict
          :param block_response: The `block_response` to convert to string.
      
          :rtype: str
          :return: The string representation of the supplied `block_response`.
          """
          string = ''
          if block_response.get('Block', {}).get('IonText') is not None:
              string += 'Block: ' + value_holder_to_string(block_response['Block']['IonText']) + ', '
      
          if block_response.get('Proof', {}).get('IonText') is not None:
              string += 'Proof: ' + value_holder_to_string(block_response['Proof']['IonText'])
      
          return '{' + string + '}'
      
      
      def digest_response_to_string(digest_response):
          """
          Returns the string representation of a given `digest_response`.
      
          :type digest_response: dict
          :param digest_response: The `digest_response` to convert to string.
      
          :rtype: str
          :return: The string representation of the supplied `digest_response`.
          """
          string = ''
          if digest_response.get('Digest') is not None:
              string += 'Digest: ' + str(digest_response['Digest']) + ', '
      
          if digest_response.get('DigestTipAddress', {}).get('IonText') is not None:
              string += 'DigestTipAddress: ' + value_holder_to_string(digest_response['DigestTipAddress']['IonText'])
      
          return '{' + string + '}'
      ```

1. Utilizzate due `.py` programmi (`get_digest.py`e`get_revision.py`) per eseguire le seguenti operazioni:
   + Richiedete un nuovo digest dal `vehicle-registration` registro.
   + Richiedi una bozza per ogni revisione del documento con VIN `1N4AL11D75C109151` dalla tabella. `VehicleRegistration`
   + Verifica le revisioni utilizzando il digest restituito e verifica ricalcolando il digest.

   Il `get_digest.py` programma contiene il codice seguente.

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from boto3 import client
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.qldb.qldb_string_utils import digest_response_to_string
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   qldb_client = client('qldb')
   
   
   def get_digest_result(name):
       """
       Get the digest of a ledger's journal.
   
       :type name: str
       :param name: Name of the ledger to operate on.
   
       :rtype: dict
       :return: The digest in a 256-bit hash value and a block address.
       """
       logger.info("Let's get the current digest of the ledger named {}".format(name))
       result = qldb_client.get_digest(Name=name)
       logger.info('Success. LedgerDigest: {}.'.format(digest_response_to_string(result)))
       return result
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       This is an example for retrieving the digest of a particular ledger.
       """
       try:
           get_digest_result(ledger_name)
       except Exception as e:
           logger.exception('Unable to get a ledger digest!')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```
**Nota**  
Usa la `get_digest_result` funzione per richiedere un riassunto che copra la *punta* corrente del diario nel tuo libro mastro. Il tip of the journal si riferisce all'ultimo blocco eseguito nel momento in cui QLDB riceve la tua richiesta.

   Il `get_revision.py` programma contiene il seguente codice.

------
#### [ 3.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from amazon.ion.simpleion import loads
   from boto3 import client
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.get_digest import get_digest_result
   from pyqldbsamples.model.sample_data import SampleData, convert_object_to_ion
   from pyqldbsamples.qldb.block_address import block_address_to_dictionary
   from pyqldbsamples.verifier import verify_document, flip_random_bit, to_base_64
   from pyqldbsamples.connect_to_ledger import create_qldb_driver
   from pyqldbsamples.qldb.qldb_string_utils import value_holder_to_string
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   qldb_client = client('qldb')
   
   
   def get_revision(ledger_name, document_id, block_address, digest_tip_address):
       """
       Get the revision data object for a specified document ID and block address.
       Also returns a proof of the specified revision for verification.
   
       :type ledger_name: str
       :param ledger_name: Name of the ledger containing the document to query.
   
       :type document_id: str
       :param document_id: Unique ID for the document to be verified, contained in the committed view of the document.
   
       :type block_address: dict
       :param block_address: The location of the block to request.
   
       :type digest_tip_address: dict
       :param digest_tip_address: The latest block location covered by the digest.
   
       :rtype: dict
       :return: The response of the request.
       """
       result = qldb_client.get_revision(Name=ledger_name, BlockAddress=block_address, DocumentId=document_id,
                                         DigestTipAddress=digest_tip_address)
       return result
   
   
   def lookup_registration_for_vin(driver, vin):
       """
       Query revision history for a particular vehicle for verification.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type vin: str
       :param vin: VIN to query the revision history of a specific registration with.
   
       :rtype: :py:class:`pyqldb.cursor.buffered_cursor.BufferedCursor`
       :return: Cursor on the result set of the statement query.
       """
       logger.info("Querying the 'VehicleRegistration' table for VIN: {}...".format(vin))
       query = 'SELECT * FROM _ql_committed_VehicleRegistration WHERE data.VIN = ?'
       return driver.execute_lambda(lambda txn: txn.execute_statement(query, convert_object_to_ion(vin)))
   
   
   def verify_registration(driver, ledger_name, vin):
       """
       Verify each version of the registration for the given VIN.
   
       :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver`
       :param driver: An instance of the QldbDriver class.
   
       :type ledger_name: str
       :param ledger_name: The ledger to get digest from.
   
       :type vin: str
       :param vin: VIN to query the revision history of a specific registration with.
   
       :raises AssertionError: When verification failed.
       """
       logger.info("Let's verify the registration with VIN = {}, in ledger = {}.".format(vin, ledger_name))
       digest = get_digest_result(ledger_name)
       digest_bytes = digest.get('Digest')
       digest_tip_address = digest.get('DigestTipAddress')
   
       logger.info('Got a ledger digest: digest tip address = {}, digest = {}.'.format(
           value_holder_to_string(digest_tip_address.get('IonText')), to_base_64(digest_bytes)))
   
       logger.info('Querying the registration with VIN = {} to verify each version of the registration...'.format(vin))
       cursor = lookup_registration_for_vin(driver, vin)
       logger.info('Getting a proof for the document.')
   
       for row in cursor:
           block_address = row.get('blockAddress')
           document_id = row.get('metadata').get('id')
   
           result = get_revision(ledger_name, document_id, block_address_to_dictionary(block_address), digest_tip_address)
           revision = result.get('Revision').get('IonText')
           document_hash = loads(revision).get('hash')
   
           proof = result.get('Proof')
           logger.info('Got back a proof: {}.'.format(proof))
   
           verified = verify_document(document_hash, digest_bytes, proof)
           if not verified:
               raise AssertionError('Document revision is not verified.')
           else:
               logger.info('Success! The document is verified.')
   
           altered_document_hash = flip_random_bit(document_hash)
           logger.info("Flipping one bit in the document's hash and assert that the document is NOT verified. "
                       "The altered document hash is: {}.".format(to_base_64(altered_document_hash)))
           verified = verify_document(altered_document_hash, digest_bytes, proof)
           if verified:
               raise AssertionError('Expected altered document hash to not be verified against digest.')
           else:
               logger.info('Success! As expected flipping a bit in the document hash causes verification to fail.')
   
           logger.info('Finished verifying the registration with VIN = {} in ledger = {}.'.format(vin, ledger_name))
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Verify the integrity of a document revision in a QLDB ledger.
       """
       registration = SampleData.VEHICLE_REGISTRATION[0]
       vin = registration['VIN']
       try:
           with create_qldb_driver(ledger_name) as driver:
               verify_registration(driver, ledger_name, vin)
       except Exception as e:
           logger.exception('Unable to verify revision.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```

------
#### [ 2.x ]

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from amazon.ion.simpleion import loads
   from boto3 import client
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.get_digest import get_digest_result
   from pyqldbsamples.model.sample_data import SampleData, convert_object_to_ion
   from pyqldbsamples.qldb.block_address import block_address_to_dictionary
   from pyqldbsamples.verifier import verify_document, flip_random_bit, to_base_64
   from pyqldbsamples.connect_to_ledger import create_qldb_session
   from pyqldbsamples.qldb.qldb_string_utils import value_holder_to_string
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   qldb_client = client('qldb')
   
   
   def get_revision(ledger_name, document_id, block_address, digest_tip_address):
       """
       Get the revision data object for a specified document ID and block address.
       Also returns a proof of the specified revision for verification.
   
       :type ledger_name: str
       :param ledger_name: Name of the ledger containing the document to query.
   
       :type document_id: str
       :param document_id: Unique ID for the document to be verified, contained in the committed view of the document.
   
       :type block_address: dict
       :param block_address: The location of the block to request.
   
       :type digest_tip_address: dict
       :param digest_tip_address: The latest block location covered by the digest.
   
       :rtype: dict
       :return: The response of the request.
       """
       result = qldb_client.get_revision(Name=ledger_name, BlockAddress=block_address, DocumentId=document_id,
                                         DigestTipAddress=digest_tip_address)
       return result
   
   
   def lookup_registration_for_vin(qldb_session, vin):
       """
       Query revision history for a particular vehicle for verification.
   
       :type qldb_session: :py:class:`pyqldb.session.qldb_session.QldbSession`
       :param qldb_session: An instance of the QldbSession class.
   
       :type vin: str
       :param vin: VIN to query the revision history of a specific registration with.
   
       :rtype: :py:class:`pyqldb.cursor.buffered_cursor.BufferedCursor`
       :return: Cursor on the result set of the statement query.
       """
       logger.info("Querying the 'VehicleRegistration' table for VIN: {}...".format(vin))
       query = 'SELECT * FROM _ql_committed_VehicleRegistration WHERE data.VIN = ?'
       parameters = [convert_object_to_ion(vin)]
       cursor = qldb_session.execute_statement(query, parameters)
       return cursor
   
   
   def verify_registration(qldb_session, ledger_name, vin):
       """
       Verify each version of the registration for the given VIN.
   
       :type qldb_session: :py:class:`pyqldb.session.qldb_session.QldbSession`
       :param qldb_session: An instance of the QldbSession class.
   
       :type ledger_name: str
       :param ledger_name: The ledger to get digest from.
   
       :type vin: str
       :param vin: VIN to query the revision history of a specific registration with.
   
       :raises AssertionError: When verification failed.
       """
       logger.info("Let's verify the registration with VIN = {}, in ledger = {}.".format(vin, ledger_name))
       digest = get_digest_result(ledger_name)
       digest_bytes = digest.get('Digest')
       digest_tip_address = digest.get('DigestTipAddress')
   
       logger.info('Got a ledger digest: digest tip address = {}, digest = {}.'.format(
           value_holder_to_string(digest_tip_address.get('IonText')), to_base_64(digest_bytes)))
   
       logger.info('Querying the registration with VIN = {} to verify each version of the registration...'.format(vin))
       cursor = lookup_registration_for_vin(qldb_session, vin)
       logger.info('Getting a proof for the document.')
   
       for row in cursor:
           block_address = row.get('blockAddress')
           document_id = row.get('metadata').get('id')
   
           result = get_revision(ledger_name, document_id, block_address_to_dictionary(block_address), digest_tip_address)
           revision = result.get('Revision').get('IonText')
           document_hash = loads(revision).get('hash')
   
           proof = result.get('Proof')
           logger.info('Got back a proof: {}.'.format(proof))
   
           verified = verify_document(document_hash, digest_bytes, proof)
           if not verified:
               raise AssertionError('Document revision is not verified.')
           else:
               logger.info('Success! The document is verified.')
   
           altered_document_hash = flip_random_bit(document_hash)
           logger.info("Flipping one bit in the document's hash and assert that the document is NOT verified. "
                       "The altered document hash is: {}.".format(to_base_64(altered_document_hash)))
           verified = verify_document(altered_document_hash, digest_bytes, proof)
           if verified:
               raise AssertionError('Expected altered document hash to not be verified against digest.')
           else:
               logger.info('Success! As expected flipping a bit in the document hash causes verification to fail.')
   
           logger.info('Finished verifying the registration with VIN = {} in ledger = {}.'.format(vin, ledger_name))
   
   
   if __name__ == '__main__':
       """
       Verify the integrity of a document revision in a QLDB ledger.
       """
       registration = SampleData.VEHICLE_REGISTRATION[0]
       vin = registration['VIN']
       try:
           with create_qldb_session() as session:
               verify_registration(session, Constants.LEDGER_NAME, vin)
       except Exception:
           logger.exception('Unable to verify revision.')
   ```

------
**Nota**  
Dopo che la `get_revision` funzione ha restituito una bozza per la revisione del documento specificata, questo programma utilizza un'API lato client per verificare tale revisione.

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python get_revision.py
   ```

Se non hai più bisogno di usare il `vehicle-registration` libro mastro, procedi con. [Passaggio 8 (opzionale): Pulisci le risorse](getting-started.python.step-8.md)

# Passaggio 8 (opzionale): Pulisci le risorse
Fase 8: Pulizia

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Puoi `vehicle-registration` continuare a utilizzare il registro. Tuttavia, se non è più necessario, è necessario eliminarlo.

**Per eliminare il registro**

1. Utilizzate il seguente programma (`delete_ledger.py`) per eliminare il `vehicle-registration` registro e tutto il suo contenuto.

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   from time import sleep
   
   from boto3 import client
   
   from pyqldbsamples.constants import Constants
   from pyqldbsamples.describe_ledger import describe_ledger
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   qldb_client = client('qldb')
   
   LEDGER_DELETION_POLL_PERIOD_SEC = 20
   
   
   def delete_ledger(ledger_name):
       """
       Send a request to QLDB to delete the specified ledger.
   
       :type ledger_name: str
       :param ledger_name: Name for the ledger to be deleted.
   
       :rtype: dict
       :return: Result from the request.
       """
       logger.info('Attempting to delete the ledger with name: {}...'.format(ledger_name))
       result = qldb_client.delete_ledger(Name=ledger_name)
       logger.info('Success.')
       return result
   
   
   def wait_for_deleted(ledger_name):
       """
       Wait for the ledger to be deleted.
   
       :type ledger_name: str
       :param ledger_name: The ledger to check on.
       """
       logger.info('Waiting for the ledger to be deleted...')
       while True:
           try:
               describe_ledger(ledger_name)
               logger.info('The ledger is still being deleted. Please wait...')
               sleep(LEDGER_DELETION_POLL_PERIOD_SEC)
           except qldb_client.exceptions.ResourceNotFoundException:
               logger.info('Success. The ledger is deleted.')
               break
   
   
   def set_deletion_protection(ledger_name, deletion_protection):
       """
       Update an existing ledger's deletion protection.
   
       :type ledger_name: str
       :param ledger_name: Name of the ledger to update.
   
       :type deletion_protection: bool
       :param deletion_protection: Enable or disable the deletion protection.
   
       :rtype: dict
       :return: Result from the request.
       """
       logger.info("Let's set deletion protection to {} for the ledger with name {}.".format(deletion_protection,
                                                                                             ledger_name))
       result = qldb_client.update_ledger(Name=ledger_name, DeletionProtection=deletion_protection)
       logger.info('Success. Ledger updated: {}'.format(result))
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Delete a ledger.
       """
       try:
           set_deletion_protection(ledger_name, False)
           delete_ledger(ledger_name)
           wait_for_deleted(ledger_name)
       except Exception as e:
           logger.exception('Unable to delete the ledger.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```
**Nota**  
Se la protezione da eliminazione è abilitata per il registro, devi prima disabilitarla prima di poter eliminare il registro utilizzando l'API QLDB.

   Il `delete_ledger.py` file dipende anche dal seguente programma (). `describe_ledger.py`

   ```
   # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   # SPDX-License-Identifier: MIT-0
   #
   # Permission is hereby granted, free of charge, to any person obtaining a copy of this
   # software and associated documentation files (the "Software"), to deal in the Software
   # without restriction, including without limitation the rights to use, copy, modify,
   # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   # permit persons to whom the Software is furnished to do so.
   #
   # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   #
   # This code expects that you have AWS credentials setup per:
   # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html
   from logging import basicConfig, getLogger, INFO
   
   from boto3 import client
   
   from pyqldbsamples.constants import Constants
   
   logger = getLogger(__name__)
   basicConfig(level=INFO)
   qldb_client = client('qldb')
   
   
   def describe_ledger(ledger_name):
       """
       Describe a ledger.
   
       :type ledger_name: str
       :param ledger_name: Name of the ledger to describe.
       """
       logger.info('describe ledger with name: {}.'.format(ledger_name))
       result = qldb_client.describe_ledger(Name=ledger_name)
       result.pop('ResponseMetadata')
       logger.info('Success. Ledger description: {}.'.format(result))
       return result
   
   
   def main(ledger_name=Constants.LEDGER_NAME):
       """
       Describe a QLDB ledger.
       """
       try:
           describe_ledger(ledger_name)
       except Exception as e:
           logger.exception('Unable to describe a ledger.')
           raise e
   
   
   if __name__ == '__main__':
       main()
   ```

1. Per eseguire il programma, immetti il comando seguente:

   ```
   python delete_ledger.py
   ```

# Utilizzo dei tipi di dati Amazon Ion in Amazon QLDB
Lavorare con Amazon Ion

**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 registro Amazon QLDB su Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Amazon QLDB archivia i dati nel formato Amazon Ion. Per lavorare con i dati in QLDB, è necessario utilizzare [una libreria Ion](http://amzn.github.io/ion-docs/libs.html) come dipendenza per un linguaggio di programmazione supportato.

In questa sezione, scopri come convertire i dati dai tipi nativi ai loro equivalenti Ion e viceversa. Questa guida di riferimento mostra esempi di codice che utilizzano il driver QLDB per elaborare i dati Ion in un registro QLDB. Include esempi di codice per Java, .NET (C\$1), Go, Node.js (TypeScript) e Python.

**Topics**
+ [

## Prerequisiti
](#driver-ion-prereqs)
+ [

## Bool
](#driver-ion-bool)
+ [

## Int
](#driver-ion-int)
+ [

## Float
](#driver-ion-float)
+ [

## Decimale
](#driver-ion-decimal)
+ [

## Timestamp
](#driver-ion-timestamp)
+ [

## Stringa
](#driver-ion-string)
+ [

## Blob
](#driver-ion-blob)
+ [

## Elenco
](#driver-ion-list)
+ [

## Struct
](#driver-ion-struct)
+ [

## Valori nulli e tipi dinamici
](#driver-ion-null)
+ [

## Conversione verso il basso in JSON
](#driver-ion-json)

## Prerequisiti


I seguenti esempi di codice presuppongono che si disponga di un'istanza del driver QLDB connessa a un registro attivo con una tabella denominata. `ExampleTable` La tabella contiene un singolo documento esistente con i seguenti otto campi:
+ ExampleBool
+ ExampleInt
+ ExampleFloat
+ ExampleDecimal
+ ExampleTimestamp
+ ExampleString
+ ExampleBlob
+ ExampleList

**Nota**  
Ai fini di questo riferimento, si supponga che il tipo memorizzato in ogni campo corrisponda al nome. In pratica, QLDB non impone definizioni di schemi o tipi di dati per i campi del documento.

## Bool


I seguenti esempi di codice mostrano come elaborare il tipo booleano Ion.

------
#### [ Java ]

```
// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();

boolean exampleBoolean = driver.execute((txn) -> {
    // Transforming a Java boolean to Ion
    boolean aBoolean = true;
    IonValue ionBool = ionSystem.newBool(aBoolean);

    // Insertion into QLDB
    txn.execute("UPDATE ExampleTable SET ExampleBool = ?", ionBool);

    // Fetching from QLDB
    Result result = txn.execute("SELECT VALUE ExampleBool from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Transforming Ion to a Java boolean
        // Cast IonValue to IonBool first
        aBoolean = ((IonBool)ionValue).booleanValue();
    }

    // exampleBoolean is now the value fetched from QLDB
    return aBoolean;
});
```

------
#### [ .NET ]

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
...

IValueFactory valueFactory = new ValueFactory();

// Transforming a C# bool to Ion.
bool nativeBool  = true;
IIonValue ionBool = valueFactory.NewBool(nativeBool);

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleBool = ?", ionBool);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleBool from ExampleTable");
});

bool? retrievedBool = null;

// Assume there is only one document in ExampleTable.
await foreach (IIonValue ionValue in selectResult)
{
    // Transforming Ion to a C# bool.
    retrievedBool = ionValue.BoolValue;
}
```

**Nota**  
Per eseguire la conversione in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in. `IResult`

------
#### [ Go ]

```
exampleBool, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
	aBool := true

	// Insertion into QLDB
	_, err = txn.Execute("UPDATE ExampleTable SET ExampleBool = ?", aBool)
	if err != nil {
		return nil, err
	}

	// Fetching from QLDB
	result, err := txn.Execute("SELECT VALUE ExampleBool FROM ExampleTable")
	if err != nil {
		return nil, err
	}

	// Assume there is only one document in ExampleTable
	if result.Next(txn) {
		var decodedResult bool
		err := ion.Unmarshal(result.GetCurrentData(), &decodedResult)
		if err != nil {
			return nil, err
		}

		// exampleBool is now the value fetched from QLDB
		return decodedResult, nil
	}
	return nil, result.Err()
})
```

------
#### [ Node.js ]

```
async function queryIonBoolean(driver: QldbDriver): Promise<boolean> {
    return (driver.executeLambda(async (txn: TransactionExecutor) => {
            // Updating QLDB
            await txn.execute("UPDATE ExampleTable SET ExampleBool = ?", true);

            // Fetching from QLDB
            const resultList: dom.Value[] = (await txn.execute("SELECT VALUE ExampleBool FROM ExampleTable")).getResultList();

            // Assume there is only one document in ExampleTable
            const ionValue: dom.Value = resultList[0];
            // Transforming Ion to a TypeScript Boolean
            const boolValue: boolean = ionValue.booleanValue();
            return boolValue;
        })
    );
}
```

------
#### [ Python ]

```
def update_and_query_ion_bool(txn):
    # QLDB can take in a Python bool
    a_bool = True

    # Insertion into QLDB
    txn.execute_statement("UPDATE ExampleTable SET ExampleBool = ?", a_bool)

    # Fetching from QLDB
    cursor = txn.execute_statement("SELECT VALUE ExampleBool FROM ExampleTable")

    # Assume there is only one document in ExampleTable
    for ion_value in cursor:
        # Ion Python bool is a child class of Python bool
        a_bool = ion_value

    # example_bool is now the value fetched from QLDB
    return a_bool


example_bool = driver.execute_lambda(lambda txn: update_and_query_ion_bool(txn))
```

------

## Int


I seguenti esempi di codice mostrano come elaborare il tipo intero Ion.

------
#### [ Java ]

```
// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();

int exampleInt = driver.execute((txn) -> {
    // Transforming a Java int to Ion
    int aInt = 256;
    IonValue ionInt = ionSystem.newInt(aInt);

    // Insertion into QLDB
    txn.execute("UPDATE ExampleTable SET ExampleInt = ?", ionInt);

    // Fetching from QLDB
    Result result = txn.execute("SELECT VALUE ExampleInt from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Transforming Ion to a Java int
        // Cast IonValue to IonInt first
        aInt = ((IonInt)ionValue).intValue();
    }

    // exampleInt is now the value fetched from QLDB
    return aInt;
});
```

------
#### [ .NET ]

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
...

IValueFactory valueFactory = new ValueFactory();

// Transforming a C# int to Ion.
int nativeInt  = 256;
IIonValue ionInt = valueFactory.NewInt(nativeInt);

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleInt = ?", ionInt);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleInt from ExampleTable");
});

int? retrievedInt = null;

// Assume there is only one document in ExampleTable.
await foreach (IIonValue ionValue in selectResult)
{
    // Transforming Ion to a C# int.
    retrievedInt = ionValue.IntValue;
}
```

**Nota**  
Per eseguire la conversione in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in. `IResult`

------
#### [ Go ]

```
exampleInt, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
	aInt := 256

	// Insertion into QLDB
	_, err = txn.Execute("UPDATE ExampleTable SET ExampleInt = ?", aInt)
	if err != nil {
		return nil, err
	}

	// Fetching from QLDB
	result, err := txn.Execute("SELECT VALUE ExampleInt FROM ExampleTable")
	if err != nil {
		return nil, err
	}

	// Assume there is only one document in ExampleTable
	if result.Next(txn) {
		var decodedResult int
		err := ion.Unmarshal(result.GetCurrentData(), &decodedResult)
		if err != nil {
			return nil, err
		}

		// exampleInt is now the value fetched from QLDB
		return decodedResult, nil
	}
	return nil, result.Err()
})
```

------
#### [ Node.js ]

```
async function queryIonInt(driver: QldbDriver): Promise<number> {
    return (driver.executeLambda(async (txn: TransactionExecutor) => {
            // Updating QLDB
            await txn.execute("UPDATE ExampleTable SET ExampleInt = ?", 256);

            // Fetching from QLDB
            const resultList: dom.Value[] = (await txn.execute("SELECT VALUE ExampleInt FROM ExampleTable")).getResultList();

            // Assume there is only one document in ExampleTable
            const ionValue: dom.Value = resultList[0];
            // Transforming Ion to a TypeScript Number
            const intValue: number = ionValue.numberValue();
            return intValue;
        })
    );
}
```

------
#### [ Python ]

```
def update_and_query_ion_int(txn):
    # QLDB can take in a Python int
    a_int = 256

    # Insertion into QLDB
    txn.execute_statement("UPDATE ExampleTable SET ExampleInt = ?", a_int)

    # Fetching from QLDB
    cursor = txn.execute_statement("SELECT VALUE ExampleInt FROM ExampleTable")

    # Assume there is only one document in ExampleTable
    for ion_value in cursor:
        # Ion Python int is a child class of Python int
        a_int = ion_value

    # example_int is now the value fetched from QLDB
    return a_int


example_int = driver.execute_lambda(lambda txn: update_and_query_ion_int(txn))
```

------

## Float


I seguenti esempi di codice mostrano come elaborare il tipo Ion float.

------
#### [ Java ]

```
// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();

float exampleFloat = driver.execute((txn) -> {
    // Transforming a Java float to Ion
    float aFloat = 256;
    IonValue ionFloat = ionSystem.newFloat(aFloat);

    // Insertion into QLDB
    txn.execute("UPDATE ExampleTable SET ExampleFloat = ?", ionFloat);

    // Fetching from QLDB
    Result result = txn.execute("SELECT VALUE ExampleFloat from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Transforming Ion to a Java float
        // Cast IonValue to IonFloat first
        aFloat = ((IonFloat)ionValue).floatValue();
    }

    // exampleFloat is now the value fetched from QLDB
    return aFloat;
});
```

------
#### [ .NET ]

**Usare C\$1 float**

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
...

IValueFactory valueFactory = new ValueFactory();

// Transforming a C# float to Ion.
float nativeFloat = 256;
IIonValue ionFloat = valueFactory.NewFloat(nativeFloat);

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleFloat = ?", ionFloat);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleFloat from ExampleTable");
});

float? retrievedFloat = null;

// Assume there is only one document in ExampleTable.
await foreach (IIonValue ionValue in selectResult)
{
    // Transforming Ion to a C# float. We cast ionValue.DoubleValue to a float
    // but be cautious, this is a down-cast and can lose precision.
    retrievedFloat = (float)ionValue.DoubleValue;
}
```

**Nota**  
Per convertire in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in. `IResult`

**Utilizzo di C\$1 double**

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
...

IValueFactory valueFactory = new ValueFactory();

// Transforming a C# double to Ion.
double nativeDouble = 256;
IIonValue ionFloat = valueFactory.NewFloat(nativeDouble);

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleFloat = ?", ionFloat);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleFloat from ExampleTable");
});

double? retrievedDouble = null;

// Assume there is only one document in ExampleTable.
await foreach (IIonValue ionValue in selectResult)
{
    // Transforming Ion to a C# double.
    retrievedDouble = ionValue.DoubleValue;
}
```

**Nota**  
Per convertire in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in. `IResult`

------
#### [ Go ]

```
exampleFloat, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
	aFloat := float32(256)

	// Insertion into QLDB
	_, err = txn.Execute("UPDATE ExampleTable SET ExampleFloat = ?", aFloat)
	if err != nil {
		return nil, err
	}

	// Fetching from QLDB
	result, err := txn.Execute("SELECT VALUE ExampleFloat FROM ExampleTable")
	if err != nil {
		return nil, err
	}

	// Assume there is only one document in ExampleTable
	if result.Next(txn) {
		// float64 would work as well
		var decodedResult float32
		err := ion.Unmarshal(result.GetCurrentData(), &decodedResult)
		if err != nil {
			return nil, err
		}

		// exampleFloat is now the value fetched from QLDB
		return decodedResult, nil
	}
	return nil, result.Err()
})
```

------
#### [ Node.js ]

```
async function queryIonFloat(driver: QldbDriver): Promise<number> {
    return (driver.executeLambda(async (txn: TransactionExecutor) => {
            // Updating QLDB
            await txn.execute("UPDATE ExampleTable SET ExampleFloat = ?", 25.6);

            // Fetching from QLDB
            const resultList: dom.Value[] = (await txn.execute("SELECT VALUE ExampleFloat FROM ExampleTable")).getResultList();

            // Assume there is only one document in ExampleTable
            const ionValue: dom.Value = resultList[0];
            // Transforming Ion to a TypeScript Number
            const floatValue: number = ionValue.numberValue();
            return floatValue;
        })
    );
}
```

------
#### [ Python ]

```
def update_and_query_ion_float(txn):
    # QLDB can take in a Python float
    a_float = float(256)

    # Insertion into QLDB
    txn.execute_statement("UPDATE ExampleTable SET ExampleFloat = ?", a_float)

    # Fetching from QLDB
    cursor = txn.execute_statement("SELECT VALUE ExampleFloat FROM ExampleTable")

    # Assume there is only one document in ExampleTable
    for ion_value in cursor:
        # Ion Python float is a child class of Python float
        a_float = ion_value

    # example_float is now the value fetched from QLDB
    return a_float


example_float = driver.execute_lambda(lambda txn: update_and_query_ion_float(txn))
```

------

## Decimale


I seguenti esempi di codice mostrano come elaborare il tipo decimale Ion.

------
#### [ Java ]

```
// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();

double exampleDouble = driver.execute((txn) -> {
    // Transforming a Java double to Ion
    double aDouble = 256;
    IonValue ionDecimal = ionSystem.newDecimal(aDouble);

    // Insertion into QLDB
    txn.execute("UPDATE ExampleTable SET ExampleDecimal = ?", ionDecimal);

    // Fetching from QLDB
    Result result = txn.execute("SELECT VALUE ExampleDecimal from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Transforming Ion to a Java double
        // Cast IonValue to IonDecimal first
        aDouble = ((IonDecimal)ionValue).doubleValue();
    }

    // exampleDouble is now the value fetched from QLDB
    return aDouble;
});
```

------
#### [ .NET ]

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
...

IValueFactory valueFactory = new ValueFactory();

// Transforming a C# decimal to Ion.
decimal nativeDecimal = 256.8723m;
IIonValue ionDecimal = valueFactory.NewDecimal(nativeDecimal);

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleDecimal = ?", ionDecimal);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleDecimal from ExampleTable");
});

decimal? retrievedDecimal = null;

// Assume there is only one document in ExampleTable.
await foreach (IIonValue ionValue in selectResult)
{
    // Transforming Ion to a C# decimal.
    retrievedDecimal = ionValue.DecimalValue;
}
```

**Nota**  
Per eseguire la conversione in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in. `IResult`

------
#### [ Go ]

```
exampleDecimal, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
	aDecimal, err := ion.ParseDecimal("256")
	if err != nil {
		return nil, err
	}

	// Insertion into QLDB
	_, err = txn.Execute("UPDATE ExampleTable SET ExampleDecimal = ?", aDecimal)
	if err != nil {
		return nil, err
	}

	// Fetching from QLDB
	result, err := txn.Execute("SELECT VALUE ExampleDecimal FROM ExampleTable")
	if err != nil {
		return nil, err
	}

	// Assume there is only one document in ExampleTable
	if result.Next(txn) {
		var decodedResult ion.Decimal
		err := ion.Unmarshal(result.GetCurrentData(), &decodedResult)
		if err != nil {
			return nil, err
		}

		// exampleDecimal is now the value fetched from QLDB
		return decodedResult, nil
	}
	return nil, result.Err()
})
```

------
#### [ Node.js ]

```
async function queryIonDecimal(driver: QldbDriver): Promise<Decimal> {
    return (driver.executeLambda(async (txn: TransactionExecutor) => {
            // Creating a Decimal value. Decimal is an Ion Type with high precision
            let ionDecimal: Decimal = dom.load("2.5d-6").decimalValue();
            // Updating QLDB
            await txn.execute("UPDATE ExampleTable SET ExampleDecimal = ?", ionDecimal);

            // Fetching from QLDB
            const resultList: dom.Value[] = (await txn.execute("SELECT VALUE ExampleDecimal FROM ExampleTable")).getResultList();

            // Assume there is only one document in ExampleTable
            const ionValue: dom.Value = resultList[0];
            // Get the Ion Decimal
            ionDecimal = ionValue.decimalValue();
            return ionDecimal;
        })
    );
}
```

**Nota**  
Puoi anche usarlo `ionValue.numberValue()` per trasformare un decimale ionico in un numero. JavaScript L'utilizzo `ionValue.numberValue()` offre prestazioni migliori, ma è meno preciso.

------
#### [ Python ]

```
def update_and_query_ion_decimal(txn):
    # QLDB can take in a Python decimal
    a_decimal = Decimal(256)

    # Insertion into QLDB
    txn.execute_statement("UPDATE ExampleTable SET ExampleDecimal = ?", a_decimal)

    # Fetching from QLDB
    cursor = txn.execute_statement("SELECT VALUE ExampleDecimal FROM ExampleTable")

    # Assume there is only one document in ExampleTable
    for ion_value in cursor:
        # Ion Python decimal is a child class of Python decimal
        a_decimal = ion_value

    # example_decimal is now the value fetched from QLDB
    return a_decimal


example_decimal = driver.execute_lambda(lambda txn: update_and_query_ion_decimal(txn))
```

------

## Timestamp


I seguenti esempi di codice mostrano come elaborare il tipo di timestamp Ion.

------
#### [ Java ]

```
// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();

Date exampleDate = driver.execute((txn) -> {
    // Transforming a Java Date to Ion
    Date aDate = new Date();
    IonValue ionTimestamp = ionSystem.newUtcTimestamp(aDate);

    // Insertion into QLDB
    txn.execute("UPDATE ExampleTable SET ExampleTimestamp = ?", ionTimestamp);

    // Fetching from QLDB
    Result result = txn.execute("SELECT VALUE ExampleTimestamp from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Transforming Ion to a Java Date
        // Cast IonValue to IonTimestamp first
        aDate = ((IonTimestamp)ionValue).dateValue();
    }

    // exampleDate is now the value fetched from QLDB
    return aDate;
});
```

------
#### [ .NET ]

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
using Amazon.IonDotnet;
...

IValueFactory valueFactory = new ValueFactory();

// Convert C# native DateTime to Ion.
DateTime nativeDateTime = DateTime.Now;

// First convert it to a timestamp object from Ion.
Timestamp timestamp = new Timestamp(nativeDateTime);
// Then convert to Ion timestamp.
IIonValue ionTimestamp = valueFactory.NewTimestamp(timestamp);

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleTimestamp = ?", ionTimestamp);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleTimestamp from ExampleTable");
});

DateTime? retrievedDateTime = null;

// Assume there is only one document in ExampleTable.
await foreach (IIonValue ionValue in selectResult)
{
    // Transforming Ion to a C# DateTime.
    retrievedDateTime = ionValue.TimestampValue.DateTimeValue;
}
```

**Nota**  
Per eseguire la conversione in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in. `IResult`

------
#### [ Go ]

```
exampleTimestamp, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
	aTimestamp := time.Date(2006, time.May, 20, 12, 30, 0, 0, time.UTC)
	if err != nil {
		return nil, err
	}

	// Insertion into QLDB
	_, err = txn.Execute("UPDATE ExampleTable SET ExampleTimestamp = ?", aTimestamp)
	if err != nil {
		return nil, err
	}

	// Fetching from QLDB
	result, err := txn.Execute("SELECT VALUE ExampleTimestamp FROM ExampleTable")
	if err != nil {
		return nil, err
	}

	// Assume there is only one document in ExampleTable
	if result.Next(txn) {
		var decodedResult ion.Timestamp
		err := ion.Unmarshal(result.GetCurrentData(), &decodedResult)
		if err != nil {
			return nil, err
		}

		// exampleTimestamp is now the value fetched from QLDB
		return decodedResult.GetDateTime(), nil
	}
	return nil, result.Err()
})
```

------
#### [ Node.js ]

```
async function queryIonTimestamp(driver: QldbDriver): Promise<Date> {
    return (driver.executeLambda(async (txn: TransactionExecutor) => {
            let exampleDateTime: Date = new Date(Date.now());
            // Updating QLDB
            await txn.execute("UPDATE ExampleTable SET ExampleTimestamp = ?", exampleDateTime);

            // Fetching from QLDB
            const resultList: dom.Value[] = (await txn.execute("SELECT VALUE ExampleTimestamp FROM ExampleTable")).getResultList();

            // Assume there is only one document in ExampleTable
            const ionValue: dom.Value = resultList[0];
            // Transforming Ion to a TypeScript Date
            exampleDateTime = ionValue.timestampValue().getDate();
            return exampleDateTime;
        })
    );
}
```

------
#### [ Python ]

```
def update_and_query_ion_timestamp(txn):
    # QLDB can take in a Python timestamp
    a_datetime = datetime.now()

    # Insertion into QLDB
    txn.execute_statement("UPDATE ExampleTable SET ExampleTimestamp = ?", a_datetime)

    # Fetching from QLDB
    cursor = txn.execute_statement("SELECT VALUE ExampleTimestamp FROM ExampleTable")

    # Assume there is only one document in ExampleTable
    for ion_value in cursor:
        # Ion Python timestamp is a child class of Python datetime
        a_timestamp = ion_value

    # example_timestamp is now the value fetched from QLDB
    return a_timestamp


example_timestamp = driver.execute_lambda(lambda txn: update_and_query_ion_timestamp(txn))
```

------

## Stringa


I seguenti esempi di codice mostrano come elaborare il tipo di stringa Ion.

------
#### [ Java ]

```
// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();

String exampleString = driver.execute((txn) -> {
    // Transforming a Java String to Ion
    String aString = "Hello world!";
    IonValue ionString = ionSystem.newString(aString);

    // Insertion into QLDB
    txn.execute("UPDATE ExampleTable SET ExampleString = ?", ionString);

    // Fetching from QLDB
    Result result = txn.execute("SELECT VALUE ExampleString from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Transforming Ion to a Java String
        // Cast IonValue to IonString first
        aString = ((IonString)ionValue).stringValue();
    }

    // exampleString is now the value fetched from QLDB
    return aString;
});
```

------
#### [ .NET ]

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
...

IValueFactory valueFactory = new ValueFactory();

// Convert C# string to Ion.
String nativeString = "Hello world!";
IIonValue ionString = valueFactory.NewString(nativeString);

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleString = ?", ionString);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleString from ExampleTable");
});

String retrievedString = null;

// Assume there is only one document in ExampleTable.
await foreach (IIonValue ionValue in selectResult)
{
    // Transforming Ion to a C# string.
    retrievedString = ionValue.StringValue;
}
```

**Nota**  
Per eseguire la conversione in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in`IResult`.

------
#### [ Go ]

```
exampleString, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
	aString := "Hello World!"

	// Insertion into QLDB
	_, err = txn.Execute("UPDATE ExampleTable SET ExampleString = ?", aString)
	if err != nil {
		return nil, err
	}

	// Fetching from QLDB
	result, err := txn.Execute("SELECT VALUE ExampleString FROM ExampleTable")
	if err != nil {
		return nil, err
	}

	// Assume there is only one document in ExampleTable
	if result.Next(txn) {
		var decodedResult string
		err := ion.Unmarshal(result.GetCurrentData(), &decodedResult)
		if err != nil {
			return nil, err
		}

		// exampleString is now the value fetched from QLDB
		return decodedResult, nil
	}
	return nil, result.Err()
})
```

------
#### [ Node.js ]

```
async function queryIonString(driver: QldbDriver): Promise<string> {
    return (driver.executeLambda(async (txn: TransactionExecutor) => {
            // Updating QLDB
            await txn.execute("UPDATE ExampleTable SET ExampleString = ?", "Hello World!");

            // Fetching from QLDB
            const resultList: dom.Value[] = (await txn.execute("SELECT VALUE ExampleString FROM ExampleTable")).getResultList();

            // Assume there is only one document in ExampleTable
            const ionValue: dom.Value = resultList[0];
            // Transforming Ion to a TypeScript String
            const stringValue: string = ionValue.stringValue();
            return stringValue;
        })
    );
}
```

------
#### [ Python ]

```
def update_and_query_ion_string(txn):
    # QLDB can take in a Python string
    a_string = "Hello world!"

    # Insertion into QLDB
    txn.execute_statement("UPDATE ExampleTable SET ExampleString = ?", a_string)

    # Fetching from QLDB
    cursor = txn.execute_statement("SELECT VALUE ExampleString FROM ExampleTable")

    # Assume there is only one document in ExampleTable
    for ion_value in cursor:
        # Ion Python string is a child class of Python string
        a_string = ion_value

    # example_string is now the value fetched from QLDB
    return a_string


example_string = driver.execute_lambda(lambda txn: update_and_query_ion_string(txn))
```

------

## Blob


I seguenti esempi di codice mostrano come elaborare il tipo di blob Ion.

------
#### [ Java ]

```
// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();

byte[] exampleBytes = driver.execute((txn) -> {
    // Transforming a Java byte array to Ion
    // Transform any arbitrary data to a byte array to store in QLDB
    String aString = "Hello world!";
    byte[] aByteArray = aString.getBytes();
    IonValue ionBlob = ionSystem.newBlob(aByteArray);

    // Insertion into QLDB
    txn.execute("UPDATE ExampleTable SET ExampleBlob = ?", ionBlob);

    // Fetching from QLDB
    Result result = txn.execute("SELECT VALUE ExampleBlob from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Transforming Ion to a Java byte array
        // Cast IonValue to IonBlob first
        aByteArray = ((IonBlob)ionValue).getBytes();
    }

    // exampleBytes is now the value fetched from QLDB
    return aByteArray;
});
```

------
#### [ .NET ]

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
using System.Text;
...

IValueFactory valueFactory = new ValueFactory();

// Transform any arbitrary data to a byte array to store in QLDB.
string nativeString = "Hello world!";
byte[] nativeByteArray = Encoding.UTF8.GetBytes(nativeString);
// Transforming a C# byte array to Ion.
IIonValue ionBlob = valueFactory.NewBlob(nativeByteArray);

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleBlob = ?", ionBlob);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleBlob from ExampleTable");
});

byte[] retrievedByteArray = null;

// Assume there is only one document in ExampleTable.
await foreach (IIonValue ionValue in selectResult)
{
    // Transforming Ion to a C# byte array.
    retrievedByteArray = ionValue.Bytes().ToArray();
}
```

**Nota**  
Per eseguire la conversione in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in. `IResult`

------
#### [ Go ]

```
exampleBlob, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
	aBlob := []byte("Hello World!")

	// Insertion into QLDB
	_, err = txn.Execute("UPDATE ExampleTable SET ExampleBlob = ?", aBlob)
	if err != nil {
		return nil, err
	}

	// Fetching from QLDB
	result, err := txn.Execute("SELECT VALUE ExampleBlob FROM ExampleTable")
	if err != nil {
		return nil, err
	}

	// Assume there is only one document in ExampleTable
	if result.Next(txn) {
		var decodedResult []byte
		err := ion.Unmarshal(result.GetCurrentData(), &decodedResult)
		if err != nil {
			return nil, err
		}

		// exampleBlob is now the value fetched from QLDB
		return decodedResult, nil
	}
	return nil, result.Err()
})
```

------
#### [ Node.js ]

```
async function queryIonBlob(driver: QldbDriver): Promise<Uint8Array> {
    return (driver.executeLambda(async (txn: TransactionExecutor) => {
            const enc = new TextEncoder();
            let blobValue: Uint8Array = enc.encode("Hello World!");
            // Updating QLDB
            await txn.execute("UPDATE ExampleTable SET ExampleBlob = ?", blobValue);

            // Fetching from QLDB
            const resultList: dom.Value[] = (await txn.execute("SELECT VALUE ExampleBlob FROM ExampleTable")).getResultList();

            // Assume there is only one document in ExampleTable
            const ionValue: dom.Value = resultList[0];
            // Transforming Ion to TypeScript Uint8Array
            blobValue = ionValue.uInt8ArrayValue();
            return blobValue;
        })
    );
}
```

------
#### [ Python ]

```
def update_and_query_ion_blob(txn):
    # QLDB can take in a Python byte array
    a_string = "Hello world!"
    a_byte_array = str.encode(a_string)

    # Insertion into QLDB
    txn.execute_statement("UPDATE ExampleTable SET ExampleBlob = ?", a_byte_array)

    # Fetching from QLDB
    cursor = txn.execute_statement("SELECT VALUE ExampleBlob FROM ExampleTable")

    # Assume there is only one document in ExampleTable
    for ion_value in cursor:
        # Ion Python blob is a child class of Python byte array
        a_blob = ion_value

    # example_blob is now the value fetched from QLDB
    return a_blob


example_blob = driver.execute_lambda(lambda txn: update_and_query_ion_blob(txn))
```

------

## Elenco


I seguenti esempi di codice mostrano come elaborare il tipo di elenco Ion.

------
#### [ Java ]

```
// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();

List<Integer> exampleList = driver.execute((txn) -> {
    // Transforming a Java List to Ion
    List<Integer> aList = new ArrayList<>();
    // Add 5 Integers to the List for the sake of example
    for (int i = 0; i < 5; i++) {
        aList.add(i);
    }
    // Create an empty Ion List
    IonList ionList = ionSystem.newEmptyList();
    // Add the 5 Integers to the Ion List
    for (Integer i : aList) {
        // Convert each Integer to Ion ints first to add it to the Ion List
        ionList.add(ionSystem.newInt(i));
    }

    // Insertion into QLDB
    txn.execute("UPDATE ExampleTable SET ExampleList = ?", (IonValue) ionList);

    // Fetching from QLDB
    Result result = txn.execute("SELECT VALUE ExampleList from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Iterate through the Ion List to map it to a Java List
        List<Integer> intList = new ArrayList<>();
        for (IonValue ionInt : (IonList)ionValue) {
            // Convert the 5 Ion ints to Java Integers
            intList.add(((IonInt)ionInt).intValue());
        }
        aList = intList;
    }

    // exampleList is now the value fetched from QLDB
    return aList;
});
```

------
#### [ .NET ]

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
using System.Collections.Generic;
...

IValueFactory valueFactory = new ValueFactory();

// Transforming a C# list to Ion.
IIonValue ionList = valueFactory.NewEmptyList();
foreach (int i in new List<int> {0, 1, 2, 3, 4})
{
    // Convert to Ion int and add to Ion list.
    ionList.Add(valueFactory.NewInt(i));
}

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleList = ?", ionList);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleList from ExampleTable");
});


List<int> retrievedList = new List<int>();
await foreach (IIonValue ionValue in selectResult)
{
    // Iterate through the Ion List to map it to a C# list.
    foreach (IIonValue ionInt in ionValue)
    {
        retrievedList.Add(ionInt.IntValue);
    }
}
```

**Nota**  
Per convertire in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in`IResult`.

------
#### [ Go ]

```
exampleList, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
	aList := []int{1, 2, 3, 4, 5}

	// Insertion into QLDB
	_, err = txn.Execute("UPDATE ExampleTable SET ExampleList = ?", aList)
	if err != nil {
		return nil, err
	}

	// Fetching from QLDB
	result, err := txn.Execute("SELECT VALUE ExampleList FROM ExampleTable")
	if err != nil {
		return nil, err
	}

	// Assume there is only one document in ExampleTable
	if result.Next(txn) {
		var decodedResult []int
		err := ion.Unmarshal(result.GetCurrentData(), &decodedResult)
		if err != nil {
			return nil, err
		}

		// exampleList is now the value fetched from QLDB
		return decodedResult, nil
	}
	return nil, result.Err()
})
```

------
#### [ Node.js ]

```
async function queryIonList(driver: QldbDriver): Promise<number[]> {
    return (driver.executeLambda(async (txn: TransactionExecutor) => {
            let listOfNumbers: number[] = [1, 2, 3, 4, 5];
            // Updating QLDB
            await txn.execute("UPDATE ExampleTable SET ExampleList = ?", listOfNumbers);

            // Fetching from QLDB
            const resultList: dom.Value[] = (await txn.execute("SELECT VALUE ExampleList FROM ExampleTable")).getResultList();

            // Assume there is only one document in ExampleTable
            const ionValue: dom.Value = resultList[0];
            // Get Ion List
            const ionList: dom.Value[] = ionValue.elements();
            // Iterate through the Ion List to map it to a JavaScript Array
            let intList: number[] = [];
            ionList.forEach(item => {
                // Transforming Ion to a TypeScript Number
                const intValue: number = item.numberValue();
                intList.push(intValue);
            });
            listOfNumbers = intList;
            return listOfNumbers;
        })
    );
}
```

------
#### [ Python ]

```
def update_and_query_ion_list(txn):
    # QLDB can take in a Python list
    a_list = list()
    for i in range(0, 5):
        a_list.append(i)

    # Insertion into QLDB
    txn.execute_statement("UPDATE ExampleTable SET ExampleList = ?", a_list)

    # Fetching from QLDB
    cursor = txn.execute_statement("SELECT VALUE ExampleList FROM ExampleTable")

    # Assume there is only one document in ExampleTable
    for ion_value in cursor:
        # Ion Python blob is a child class of Python list
        a_list = ion_value

    # example_list is now the value fetched from QLDB
    return a_list


example_list = driver.execute_lambda(lambda txn: update_and_query_ion_list(txn))
```

------

## Struct


In QLDB`struct`, i tipi di dati sono particolarmente unici rispetto agli altri tipi di ioni. I documenti di primo livello inseriti in una tabella devono essere del tipo. `struct` Un campo del documento può anche memorizzare un file annidato`struct`.

Per semplicità, gli esempi seguenti definiscono un documento che contiene solo `ExampleInt` i campi `ExampleString` and.

------
#### [ Java ]

```
class ExampleStruct {
    public String exampleString;
    public int exampleInt;

    public ExampleStruct(String exampleString, int exampleInt) {
        this.exampleString = exampleString;
        this.exampleInt = exampleInt;
    }
}

// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();

ExampleStruct examplePojo = driver.execute((txn) -> {
    // Transforming a POJO to Ion
    ExampleStruct aPojo = new ExampleStruct("Hello world!", 256);
    // Create an empty Ion struct
    IonStruct ionStruct = ionSystem.newEmptyStruct();
    // Map the fields of the POJO to Ion values and put them in the Ion struct
    ionStruct.add("ExampleString", ionSystem.newString(aPojo.exampleString));
    ionStruct.add("ExampleInt", ionSystem.newInt(aPojo.exampleInt));

    // Insertion into QLDB
    txn.execute("INSERT INTO ExampleTable ?", ionStruct);

    // Fetching from QLDB
    Result result = txn.execute("SELECT * from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Map the fields of the Ion struct to Java values and construct a new POJO
        ionStruct = (IonStruct)ionValue;
        IonString exampleString = (IonString)ionStruct.get("ExampleString");
        IonInt exampleInt = (IonInt)ionStruct.get("ExampleInt");
        aPojo = new ExampleStruct(exampleString.stringValue(), exampleInt.intValue());
    }

    // examplePojo is now the document fetched from QLDB
    return aPojo;
});
```

In alternativa, puoi usare la [libreria Jackson](https://github.com/FasterXML/jackson-dataformats-binary/tree/master/ion) per mappare i tipi di dati da e verso Ion. La libreria supporta gli altri tipi di dati Ion, ma questo esempio si concentra sul `struct` tipo.

```
class ExampleStruct {
    public String exampleString;
    public int exampleInt;

    @JsonCreator
    public ExampleStruct(@JsonProperty("ExampleString") String exampleString,
                         @JsonProperty("ExampleInt") int exampleInt) {
        this.exampleString = exampleString;
        this.exampleInt = exampleInt;
    }

    @JsonProperty("ExampleString")
    public String getExampleString() {
        return this.exampleString;
    }

    @JsonProperty("ExampleInt")
    public int getExampleInt() {
        return this.exampleInt;
    }
}

// Instantiate an IonSystem from the Ion library
IonSystem ionSystem = IonSystemBuilder.standard().build();
// Instantiate an IonObjectMapper from the Jackson library
IonObjectMapper ionMapper = new IonValueMapper(ionSystem);

ExampleStruct examplePojo = driver.execute((txn) -> {
    // Transforming a POJO to Ion
    ExampleStruct aPojo = new ExampleStruct("Hello world!", 256);
    IonValue ionStruct;
    try  {
        // Use the mapper to convert Java objects into Ion
        ionStruct = ionMapper.writeValueAsIonValue(aPojo);
    } catch (IOException e) {
        // Wrap the exception and throw it for the sake of simplicity in this example
        throw new RuntimeException(e);
    }

    // Insertion into QLDB
    txn.execute("INSERT INTO ExampleTable ?", ionStruct);

    // Fetching from QLDB
    Result result = txn.execute("SELECT * from ExampleTable");
    // Assume there is only one document in ExampleTable
    for (IonValue ionValue : result)
    {
        // Use the mapper to convert Ion to Java objects
        try {
            aPojo = ionMapper.readValue(ionValue, ExampleStruct.class);
        } catch (IOException e) {
            // Wrap the exception and throw it for the sake of simplicity in this example
            throw new RuntimeException(e);
        }
    }

    // examplePojo is now the document fetched from QLDB
    return aPojo;
});
```

------
#### [ .NET ]

Utilizza la libreria [Amazon.Qldb.Driver.Serialization](https://www.nuget.org/packages/Amazon.QLDB.Driver.Serialization/) per mappare i tipi di dati C\$1 nativi da e verso Ion. La libreria supporta gli altri tipi di dati Ion, ma questo esempio si concentra sul tipo. `struct`

```
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;
...

IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
    .WithLedger("vehicle-registration")
    // Add Serialization library
    .WithSerializer(new ObjectSerializer())
    .Build();

// Creating a C# POCO.
ExampleStruct exampleStruct = new ExampleStruct
{
    ExampleString = "Hello world!",
    ExampleInt = 256
};

IAsyncResult<ExampleStruct> selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute(txn.Query<Document>("UPDATE ExampleTable SET ExampleStruct = ?", exampleStruct));

    // Fetching from QLDB.
    return await txn.Execute(txn.Query<ExampleStruct>("SELECT VALUE ExampleStruct from ExampleTable"));
});

await foreach (ExampleStruct row in selectResult)
{
    Console.WriteLine(row.ExampleString);
    Console.WriteLine(row.ExampleInt);
}
```

**Nota**  
Per eseguire la conversione in codice sincrono, rimuovete le `async` parole chiave `await` and e modificate il `IAsyncResult` tipo in`IResult`.

In alternativa, puoi usare [Amazon. IonDotnetLibreria.Builders](https://www.nuget.org/packages/Amazon.IonDotnet/) per elaborare i tipi di dati Ion.

```
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
...

IValueFactory valueFactory = new ValueFactory();

// Creating Ion struct.
IIonValue ionStruct = valueFactory.NewEmptyStruct();
ionStruct.SetField("ExampleString", valueFactory.NewString("Hello world!"));
ionStruct.SetField("ExampleInt", valueFactory.NewInt(256));

IAsyncResult selectResult = await driver.Execute(async txn =>
{
    // Insertion into QLDB.
    await txn.Execute("UPDATE ExampleTable SET ExampleStruct = ?", ionStruct);

    // Fetching from QLDB.
    return await txn.Execute("SELECT VALUE ExampleStruct from ExampleTable");
});

string retrievedString = null;
int? retrievedInt = null;

await foreach (IIonValue ionValue in selectResult)
{
    retrievedString = ionValue.GetField("ExampleString").StringValue;
    retrievedInt = ionValue.GetField("ExampleInt").IntValue;
}
```

------
#### [ Go ]

```
exampleStruct, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
	aStruct := map[string]interface{} {
		"ExampleString": "Hello World!",
		"ExampleInt": 256,
	}

	// Insertion into QLDB
	_, err = txn.Execute("UPDATE ExampleTable SET ExampleStruct = ?", aStruct)
	if err != nil {
		return nil, err
	}

	// Fetching from QLDB
	result, err := txn.Execute("SELECT VALUE ExampleStruct FROM ExampleTable")
	if err != nil {
		return nil, err
	}

	// Assume there is only one document in ExampleTable
	if result.Next(txn) {
		var decodedResult map[string]interface{}
		err := ion.Unmarshal(result.GetCurrentData(), &decodedResult)
		if err != nil {
			return nil, err
		}

		// exampleStruct is now the value fetched from QLDB
		return decodedResult, nil
	}
	return nil, result.Err()
})
```

------
#### [ Node.js ]

```
async function queryIonStruct(driver: QldbDriver): Promise<any> {
    let exampleStruct: any = {stringValue: "Hello World!", intValue: 256};
    return (driver.executeLambda(async (txn: TransactionExecutor) => {
            // Inserting into QLDB
            await txn.execute("INSERT INTO ExampleTable ?", exampleStruct);

            // Fetching from QLDB
            const resultList: dom.Value[] = (await txn.execute("SELECT * FROM ExampleTable")).getResultList();

            // Assume there is only one document in ExampleTable
            const ionValue: dom.Value = resultList[0];
            // We can get all the keys of Ion struct and their associated values
            const ionFieldNames: string[] = ionValue.fieldNames();

            // Getting key and value of Ion struct to TypeScript String and Number
            const nativeStringVal: string = ionValue.get(ionFieldNames[0]).stringValue();
            const nativeIntVal: number = ionValue.get(ionFieldNames[1]).numberValue();
            // Alternatively, we can access to Ion struct fields, using their literal field names:
            //   const nativeStringVal = ionValue.get("stringValue").stringValue();
            //   const nativeIntVal = ionValue.get("intValue").numberValue();

            exampleStruct = {[ionFieldNames[0]]: nativeStringVal, [ionFieldNames[1]]: nativeIntVal};
            return exampleStruct;
        })
    );
}
```

------
#### [ Python ]

```
def update_and_query_ion_struct(txn):
    # QLDB can take in a Python struct
    a_struct = {"ExampleString": "Hello world!", "ExampleInt": 256}

    # Insertion into QLDB
    txn.execute_statement("UPDATE ExampleTable SET ExampleStruct = ?", a_struct)

    # Fetching from QLDB
    cursor = txn.execute_statement("SELECT VALUE ExampleStruct FROM ExampleTable")

    # Assume there is only one document in ExampleTable
    for ion_value in cursor:
        # Ion Python struct is a child class of Python struct
        a_struct = ion_value

    # example_struct is now the value fetched from QLDB
    return a_struct


example_struct = driver.execute_lambda(lambda txn: update_and_query_ion_struct(txn))
```

------

## Valori nulli e tipi dinamici


QLDB supporta contenuti aperti e non impone definizioni di schemi o tipi di dati per i campi del documento. È inoltre possibile memorizzare valori nulli di Ion in un documento QLDB. Tutti gli esempi precedenti presuppongono che ogni tipo di dati restituito sia noto e non sia nullo. Gli esempi seguenti mostrano come utilizzare Ion quando il tipo di dati non è noto o è probabilmente nullo.

------
#### [ Java ]

```
// Empty variables
String exampleString = null;
Integer exampleInt = null;

// Assume ionValue is some queried data from QLDB
IonValue ionValue = null;

// Check the value type and assign it to the variable if it is not null
if (ionValue.getType() == IonType.STRING) {
    if (ionValue.isNullValue()) {
        exampleString = null;
    } else {
        exampleString = ((IonString)ionValue).stringValue();
    }
} else if (ionValue.getType() == IonType.INT) {
    if (ionValue.isNullValue()) {
        exampleInt = null;
    } else {
        exampleInt = ((IonInt)ionValue).intValue();
    }
};

// Creating null values
IonSystem ionSystem = IonSystemBuilder.standard().build();

// A null value still has an Ion type
IonString ionString;
if (exampleString == null) {
    // Specifically a null string
    ionString = ionSystem.newNullString();
} else {
    ionString = ionSystem.newString(exampleString);
}

IonInt ionInt;
if (exampleInt == null) {
    // Specifically a null int
    ionInt = ionSystem.newNullInt();
} else {
    ionInt = ionSystem.newInt(exampleInt);
}

// Special case regarding null!
// There is a generic null type which has Ion type 'Null'.
// The above null values still have the types 'String' and 'Int'
IonValue specialNull = ionSystem.newNull();
if (specialNull.getType() == IonType.NULL) {
    // This is true!
}
if (specialNull.isNullValue()) {
    // This is also true!
}
if (specialNull.getType() == IonType.STRING || specialNull.getType() == IonType.INT) {
    // This is false!
}
```

------
#### [ .NET ]

```
// Empty variables.
string exampleString = null;
int? exampleInt = null;

// Assume ionValue is some queried data from QLDB.
IIonValue ionValue;

if (ionValue.Type() == IonType.String)
{
    exampleString = ionValue.StringValue;
}
else if (ionValue.Type() == IonType.Int)
{
    if (ionValue.IsNull)
    {
        exampleInt = null;
    }
    else
    {
        exampleInt = ionValue.IntValue;
    }
};

// Creating null values.
IValueFactory valueFactory = new ValueFactory();

// A null value still has an Ion type.
IIonValue ionString = valueFactory.NewString(exampleString);

IIonValue ionInt;
if (exampleInt == null)
{
    // Specifically a null int.
    ionInt = valueFactory.NewNullInt();
}
else
{
    ionInt = valueFactory.NewInt(exampleInt.Value);
}

// Special case regarding null!
// There is a generic null type which has Ion type 'Null'.
IIonValue specialNull = valueFactory.NewNull();
if (specialNull.Type() == IonType.Null) {
    // This is true!
}
if (specialNull.IsNull) {
    // This is also true!
}
```

------
#### [ Go ]

**Limitazioni del marshalling**

In Go, `nil` i valori non mantengono il loro tipo quando li organizzi e poi li annulli. Un `nil` valore porta a Ion null, ma Ion null demarshalla a un valore zero anziché. `nil`

```
ionNull, err := ion.MarshalText(nil) // ionNull is set to ion null
if err != nil {
    return
}

var result int
err = ion.Unmarshal(ionNull, &result) // result unmarshals to 0
if err != nil {
    return
}
```

------
#### [ Node.js ]

```
// Empty variables
let exampleString: string;
let exampleInt: number;

// Assume ionValue is some queried data from QLDB
// Check the value type and assign it to the variable if it is not null
if (ionValue.getType() === IonTypes.STRING) {
    if (ionValue.isNull()) {
        exampleString = null;
    } else {
        exampleString = ionValue.stringValue();
    }
} else if (ionValue.getType() === IonTypes.INT) {
    if (ionValue.isNull()) {
        exampleInt = null;
    } else {
        exampleInt = ionValue.numberValue();
    }
}

// Creating null values
if (exampleString === null) {
    ionString = dom.load('null.string');
} else {
    ionString = dom.load.of(exampleString);
}

if (exampleInt === null) {
    ionInt = dom.load('null.int');
} else {
    ionInt = dom.load.of(exampleInt);
}

// Special case regarding null!
// There is a generic null type which has Ion type 'Null'.
// The above null values still have the types 'String' and 'Int'
specialNull: dom.Value = dom.load("null.null");
if (specialNull.getType() === IonType.NULL) {
    // This is true!
}
if (specialNull.getType() === IonType.STRING || specialNull.getType() === IonType.INT) {
    // This is false!
}
```

------
#### [ Python ]

```
# Empty variables
example_string = None
example_int = None

# Assume ion_value is some queried data from QLDB
# Check the value type and assign it to the variable if it is not null
if ion_value.ion_type == IonType.STRING:
    if isinstance(ion_value, IonPyNull):
        example_string = None
    else:
        example_string = ion_value
elif ion_value.ion_type == IonType.INT:
    if isinstance(ion_value, IonPyNull):
        example_int = None
    else:
        example_int = ion_value

# Creating Ion null values
if example_string is None:
    # Specifically a null string
    ion_string = loads("null.string")
else:
    # QLDB can take in Python string
    ion_string = example_string
if example_int is None:
    # Specifically a null int
    ion_int = loads("null.int")
else:
    # QLDB can take in Python int
    ion_int = example_int


# Special case regarding null!
# There is a generic null type which has Ion type 'Null'.
# The above null values still have the types 'String' and 'Int'
special_null = loads("null.null")
if special_null.ion_type == IonType.NULL:
    # This is true!
if special_null.ion_type == IonType.STRING or special_null.ion_type == IonType.INT:
    # This is false!
```

------

## Conversione verso il basso in JSON


Se la tua applicazione richiede la compatibilità JSON, puoi convertire i dati di Amazon Ion in JSON. Tuttavia, la conversione di Ion in JSON comporta perdite in alcuni casi in cui i dati utilizzano tipi di ioni avanzati che non esistono in JSON.

*Per informazioni dettagliate sulle regole di conversione da Ion a JSON, consulta [Down-converting to JSON](https://amzn.github.io/ion-docs/guides/cookbook.html#down-converting-to-json) nell'Amazon Ion Cookbook.*