

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Controlador de Amazon QLDB para Go: libro de recetas de referencia
<a name="driver-cookbook-golang"></a>

**importante**  
Aviso de fin del soporte: los clientes actuales podrán utilizar Amazon QLDB hasta que finalice el soporte, el 31 de julio de 2025. Para obtener más información, consulte [Migración de un registro de Amazon QLDB a Amazon Aurora](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/) PostgreSQL.

Esta guía de referencia muestra los casos de uso más comunes del controlador de Amazon QLDB para Go. En él se proporcionan ejemplos de código Go que muestran cómo utilizar el controlador para ejecutar operaciones básicas de *creación, lectura, actualización y eliminación* (CRUD). También incluye ejemplos de código para procesar datos de Amazon Ion. Además, esta guía destaca las prácticas recomendadas para hacer que las transacciones sean idempotentes e implementar restricciones de exclusividad.

**nota**  
Cuando proceda, algunos pasos incluyen ejemplos de código diferentes para cada versión principal compatible del controlador de QLDB para Go.

**Contents**
+ [

## Importación del controlador
](#cookbook-golang.importing)
+ [

## Instanciación del controlador
](#cookbook-golang.instantiating)
+ [

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

### Creación de tablas
](#cookbook-golang.crud.creating-tables)
  + [

### Creación de índices
](#cookbook-golang.crud.creating-indexes)
  + [

### Lectura de documentos
](#cookbook-golang.crud.reading)
    + [

#### Uso de parámetros de consulta
](#cookbook-golang.reading-using-params)
  + [

### Inserción de documentos
](#cookbook-golang.crud.inserting)
    + [

#### Insertar varios documentos en una instrucción
](#cookbook-golang.crud.inserting.multiple)
  + [

### Actualización de documentos
](#cookbook-golang.crud.updating)
  + [

### Eliminación de documentos
](#cookbook-golang.crud.deleting)
  + [

### Ejecutar varias instrucciones en una transacción
](#cookbook-golang.crud.multi-statement)
  + [

### Lógica de reintentos
](#cookbook-golang.crud.retry-logic)
  + [

### Implementación de restricciones de exclusividad
](#cookbook-golang.crud.uniqueness-constraints)
+ [

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

### Importación del módulo Ion
](#cookbook-golang.ion.import)
  + [

### Creación de tipos de Ion
](#cookbook-golang.ion.creating-types)
  + [

### Obtener el binario de Ion
](#cookbook-golang.ion.getting-binary)
  + [

### Obtener texto de Ion
](#cookbook-golang.ion.getting-text)

## Importación del controlador
<a name="cookbook-golang.importing"></a>

El siguiente ejemplo de código importa el controlador y otros paquetes necesarios. AWS 

------
#### [ 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**  
En este ejemplo también se importa el paquete Amazon Ion (`amzn/ion-go/ion`). Necesita este paquete para procesar los datos de Ion al ejecutar algunas operaciones de datos de esta referencia. Para obtener más información, consulte [Trabajar con Amazon Ion](#cookbook-golang.ion).

## Instanciación del controlador
<a name="cookbook-golang.instantiating"></a>

En el siguiente ejemplo de código, se crea una instancia del controlador que se conecta a un nombre de libro mayor especificado en una Región de AWS determinada.

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

------

## Operaciones CRUD
<a name="cookbook-golang.crud"></a>

La QLDB ejecuta operaciones de *creación, lectura, actualización y eliminación* (CRUD) como parte de una transacción.

**aviso**  
Como práctica recomendada, haga que sus transacciones de escritura sean estrictamente idempotentes.

**Hacer que las transacciones sean idempotentes**

Le recomendamos que haga que las transacciones de escritura sean idempotentes para evitar cualquier efecto secundario inesperado en caso de reintentos. Una transacción es *idempotente* si puede ejecutarse varias veces y producir resultados idénticos cada vez.

Por ejemplo, pensemos en una transacción que inserta un documento en una tabla llamada `Person`. La transacción debe comprobar primero si el documento ya existe o no en la tabla. Sin esta comprobación, la tabla podría terminar con documentos duplicados.

Supongamos que QLDB confirma correctamente la transacción en el servidor pero el cliente agota el tiempo de espera mientras espera una respuesta. Si la transacción no es idempotente, se podría insertar el mismo documento más de una vez en caso de volver a intentarlo.

**Uso de índices para evitar escanear tablas completas**

También le recomendamos que ejecute instrucciones con una frase de predicado `WHERE` utilizando un operador de *igualdad* en un campo indexado o un ID de documento; por ejemplo, `WHERE indexedField = 123` o `WHERE indexedField IN (456, 789)`. Sin esta búsqueda indexada, la QLDB necesita escanear las tablas, lo que puede provocar tiempos de espera de las transacciones o conflictos de *control de concurrencia optimista* (OCC).

Para obtener más información acerca de OCC, consulte [Modelo de concurrencia de Amazon QLDB](concurrency.md).

**Transacciones creadas de forma implícita**

La función [QLDBDriver.Execute](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver#QLDBDriver.Execute) acepta una función lambda que recibe una instancia de [Transaction](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver#Transaction), que se puede utilizar para ejecutar sentencias. La instancia de `Transaction` envuelve una transacción creada de forma implícita.

Puede ejecutar instrucciones dentro de la función de Lambda mediante la función `Transaction.Execute`. El controlador confirma implícitamente la transacción cuando vuelve la función de Lambda.

En las siguientes secciones se muestra cómo ejecutar operaciones CRUD básicas, especificar una lógica de reintento personalizada e implementar restricciones de exclusividad.

**Contents**
+ [

### Creación de tablas
](#cookbook-golang.crud.creating-tables)
+ [

### Creación de índices
](#cookbook-golang.crud.creating-indexes)
+ [

### Lectura de documentos
](#cookbook-golang.crud.reading)
  + [

#### Uso de parámetros de consulta
](#cookbook-golang.reading-using-params)
+ [

### Inserción de documentos
](#cookbook-golang.crud.inserting)
  + [

#### Insertar varios documentos en una instrucción
](#cookbook-golang.crud.inserting.multiple)
+ [

### Actualización de documentos
](#cookbook-golang.crud.updating)
+ [

### Eliminación de documentos
](#cookbook-golang.crud.deleting)
+ [

### Ejecutar varias instrucciones en una transacción
](#cookbook-golang.crud.multi-statement)
+ [

### Lógica de reintentos
](#cookbook-golang.crud.retry-logic)
+ [

### Implementación de restricciones de exclusividad
](#cookbook-golang.crud.uniqueness-constraints)

### Creación de tablas
<a name="cookbook-golang.crud.creating-tables"></a>

```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("CREATE TABLE Person")
})
```

### Creación de índices
<a name="cookbook-golang.crud.creating-indexes"></a>

```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("CREATE INDEX ON Person(GovId)")
})
```

### Lectura de documentos
<a name="cookbook-golang.crud.reading"></a>

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

#### Uso de parámetros de consulta
<a name="cookbook-golang.reading-using-params"></a>

En el siguiente ejemplo de código, se utiliza un parámetro de consulta de 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)
}
```

En el siguiente ejemplo de código se utilizan varios parámetros de consulta.

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

En el siguiente ejemplo de código, se utiliza una lista de parámetros de consulta.

```
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**  
Cuando ejecuta una consulta sin una búsqueda indexada, se invoca un escaneo completo de la tabla. En este ejemplo, se recomienda tener un [índice](ql-reference.create-index.md) en el campo `GovId` para optimizar el rendimiento. Sin un índice en `GovId`, las consultas pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.

### Inserción de documentos
<a name="cookbook-golang.crud.inserting"></a>

El siguiente ejemplo de código inserta los tipos de datos nativos.

```
_, 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
})
```

Esta transacción inserta un documento en la tabla `Person`. Antes de insertar, compruebe primero si el documento ya existe en la tabla. **Esta comprobación hace que la transacción sea de naturaleza idempotente.** Incluso si realiza esta transacción varias veces, no provocará ningún efecto secundario no deseado.

**nota**  
En este ejemplo, se recomienda tener un índice en el campo `GovId` para optimizar el rendimiento. Sin un índice en `GovId`, las instrucciones pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.

#### Insertar varios documentos en una instrucción
<a name="cookbook-golang.crud.inserting.multiple"></a>

Para insertar varios documentos mediante una sola instrucción [INSERT](ql-reference.insert.md), puede pasar un parámetro del tipo [list](driver-working-with-ion.md#driver-ion-list) a la instrucción de la siguiente manera.

```
// people is a list
txn.Execute("INSERT INTO People ?", people)
```

No coloque el marcador de posición variable (`?`) entre corchetes de doble ángulo (`<<...>>`) al pasar una lista. En las instrucciones PartiQL manuales, los corchetes de doble ángulo indican una colección desordenada conocida como *bolsa*.

### Actualización de documentos
<a name="cookbook-golang.crud.updating"></a>

El siguiente ejemplo de código utiliza tipos de datos nativos.

```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", "John", "TOYENC486FH")
})
```

**nota**  
En este ejemplo, se recomienda tener un índice en el campo `GovId` para optimizar el rendimiento. Sin un índice en `GovId`, las instrucciones pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.

### Eliminación de documentos
<a name="cookbook-golang.crud.deleting"></a>

El siguiente ejemplo de código utiliza tipos de datos nativos.

```
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
  return txn.Execute("DELETE FROM Person WHERE GovId = ?", "TOYENC486FH")
})
```

**nota**  
En este ejemplo, se recomienda tener un índice en el campo `GovId` para optimizar el rendimiento. Sin un índice en `GovId`, las instrucciones pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.

### Ejecutar varias instrucciones en una transacción
<a name="cookbook-golang.crud.multi-statement"></a>

```
// 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
}
```

### Lógica de reintentos
<a name="cookbook-golang.crud.retry-logic"></a>

La función del controlador `Execute` tiene un mecanismo de reintento integrado que reintenta la transacción si se produce una excepción que se pueda volver a intentar (como tiempos de espera o conflictos de OCC). El número máximo de reintentos y la estrategia de retardo se pueden configurar.

El límite de reintentos predeterminado es`4`, y la estrategia de retroceso predeterminada, se basa en [ExponentialBackoffStrategy](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver#ExponentialBackoffStrategy)milisegundos. `10` Puede establecer la política de reintentos por instancia del controlador y también por transacción mediante una instancia de. [RetryPolicy](https://pkg.go.dev/github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver#RetryPolicy)

El siguiente ejemplo de código especifica la lógica de reintentos con un límite de reintentos personalizado y una estrategia de retardo personalizada para una instancia del controlador.

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

El siguiente ejemplo de código especifica la lógica de reintentos con un límite de reintentos personalizado y una estrategia de retardo personalizada para una función anónima particular. La función `SetRetryPolicy` anula la política de reintentos establecida para la instancia del controlador.

```
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")
  })
}
```

### Implementación de restricciones de exclusividad
<a name="cookbook-golang.crud.uniqueness-constraints"></a>

QLDB no admite índices únicos, pero puede implementar este comportamiento en su aplicación.

Suponga que desea implementar una restricción de exclusividad en el campo `GovId` de la tabla `Person`. Para ello, puede escribir una transacción que haga lo siguiente:

1. Afirme que en la tabla no hay documentos existentes con un `GovId` especificado.

1. Insertar el documento si se aprueba la afirmación.

Si una transacción competidora supera la afirmación simultáneamente, solo una de las transacciones se confirmará correctamente. La otra transacción fallará y se producirá una excepción de conflicto de OCC.

En el siguiente ejemplo de código, se muestra cómo implementar esta lógica de restricción de exclusividad.

```
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**  
En este ejemplo, se recomienda tener un índice en el campo `GovId` para optimizar el rendimiento. Sin un índice en `GovId`, las instrucciones pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.

## Trabajar con Amazon Ion
<a name="cookbook-golang.ion"></a>

Las siguientes secciones muestran cómo usar el módulo Amazon Ion para procesar los datos de Ion.

**Contents**
+ [

### Importación del módulo Ion
](#cookbook-golang.ion.import)
+ [

### Creación de tipos de Ion
](#cookbook-golang.ion.creating-types)
+ [

### Obtener el binario de Ion
](#cookbook-golang.ion.getting-binary)
+ [

### Obtener texto de Ion
](#cookbook-golang.ion.getting-text)

### Importación del módulo Ion
<a name="cookbook-golang.ion.import"></a>

```
import "github.com/amzn/ion-go/ion"
```

### Creación de tipos de Ion
<a name="cookbook-golang.ion.creating-types"></a>

La biblioteca de Ion para Go actualmente no es compatible con el modelo de objetos de documento (DOM), por lo que no se pueden crear tipos de datos de Ion. Pero puede serializar y anular la serialización entre los tipos nativos de Go y binario de Ion cuando trabaja con la QLDB.

### Obtener el binario de Ion
<a name="cookbook-golang.ion.getting-binary"></a>

```
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]
```

### Obtener texto de Ion
<a name="cookbook-golang.ion.getting-text"></a>

```
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"}
```

Para obtener más información sobre Ion, consulte la [documentación de Amazon Ion](http://amzn.github.io/ion-docs/) en GitHub. Para ver más ejemplos de código sobre cómo trabajar con Ion en QLDB, consulte [Uso de tipos de datos de Amazon Ion en Amazon QLDB](driver-working-with-ion.md).