Controlador Amazon QLDB para Java: referencia de libro de cocina - Amazon Quantum Ledger Database (Amazon QLDB)

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 Amazon QLDB para Java: referencia de libro de cocina

Esta guía de referencia muestra casos de uso comunes del controlador Amazon QLDB para Java. Proporciona ejemplos de código Java que muestran cómo utilizar el controlador para ejecutarcrear, leer, actualizar y eliminar(CRUD). También incluye ejemplos de código para procesar datos de Amazon Ion. Además, esta guía destaca las mejores prácticas para hacer que las transacciones sean idempotentes e implementar restricciones de singularidad.

nota

En su caso, algunos casos de uso tienen ejemplos de código diferentes para cada versión principal compatible del controlador QLDB para Java.

Importación del controlador

En el siguiente ejemplo de código se importa el controlador, el cliente de sesión QLDB, los paquetes de Amazon Ion y otras dependencias relacionadas.

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;

Creación de una instancia del controlador

En el siguiente ejemplo de código se crea una instancia de controlador que se conecta a un nombre de libro mayor especificado y utiliza especificadosLógica de reintentocon un límite de reintentos personalizado.

nota

En este ejemplo también se crea una instancia de un objeto de sistema Amazon Ion (IonSystem). Necesita este objeto para procesar datos de Ion al ejecutar algunas operaciones de datos en esta referencia. Para obtener más información, consulte Trabajo con Amazon Ion.

2.x
QldbDriver qldbDriver = QldbDriver.builder() .ledger("vehicle-registration") .transactionRetryPolicy(RetryPolicy .builder() .maxRetries(3) .build()) .sessionClientBuilder(QldbSessionClient.builder()) .build(); IonSystem SYSTEM = IonSystemBuilder.standard().build();
1.x
PooledQldbDriver qldbDriver = PooledQldbDriver.builder() .withLedger("vehicle-registration") .withRetryLimit(3) .withSessionClientBuilder(AmazonQLDBSessionClientBuilder.standard()) .build(); IonSystem SYSTEM = IonSystemBuilder.standard().build();

Operaciones CRUD

Ejecuciones de QLDBcrear, leer, actualizar y eliminar(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 efectos secundarios inesperados en caso de reintentos. Una transacción esidempotentesi puede ejecutarse varias veces y producir resultados idénticos cada vez.

Por ejemplo, considere una transacción que inserta un documento en una tabla denominadaPerson. En primer lugar, la transacción debe comprobar si el documento ya existe en la tabla o no. Sin esta comprobación, la tabla podría terminar con documentos duplicados.

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

Uso de índices para evitar análisis completos de tablas

También le recomendamos que ejecute extractos con unWHEREcláusula predicado mediante unigualdadoperador en un campo indexado o un ID de documento; por ejemplo,WHERE indexedField = 123oWHERE indexedField IN (456, 789). Sin esta búsqueda indexada, QLDB necesita realizar un análisis de tabla, lo que puede provocar tiempos de espera de transacción ocontrol de concurrencia optimista(OCC) conflictos.

Para obtener más información acerca de OCC, consulteModelo de simultaneidad de Amazon QLDB.

Transacciones creadas implícitamente

LaQldbDriver.executeacepta una función lambda que recibe una instancia deAlbacea, que puede utilizar para ejecutar sentencias. LaExecutorinstancia envuelve una transacción creada implícitamente.

Puede ejecutar sentencias dentro de la función lambda mediante elExecutor.executemétodo. El conductor confirma implícitamente la transacción cuando vuelve la función lambda.

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

nota

En su caso, estas secciones proporcionan ejemplos de código del procesamiento de datos de Amazon Ion utilizando la biblioteca Ion integrada y la biblioteca mapeadora Jackson Ion. Para obtener más información, consulte Trabajo con Amazon Ion.

Creación de tablas

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

Creación de índices

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

Lectura de documentos

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

Uso de parámetros de consulta

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

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

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

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

qldbDriver.execute(txn -> { final List<IonValue> parameters = new ArrayList<>(); parameters.add(SYSTEM.newString("TOYENC486FH")); parameters.add(SYSTEM.newString("ROEE1")); parameters.add(SYSTEM.newString("YH844")); Result result = txn.execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", parameters); IonStruct person = (IonStruct) result.iterator().next(); System.out.println(person.get("GovId")); // prints TOYENC486FH System.out.println(person.get("FirstName")); // prints Brent });
// Assumes that Person table has documents as follows: // {GovId: "TOYENC486FH", FirstName: "Brent" } qldbDriver.execute(txn -> { try { Result result = txn.execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'"); Person person = MAPPER.readValue(result.iterator().next(), Person.class); System.out.println(person.getFirstName()); // prints Brent System.out.println(person.getGovId()); // prints TOYENC486FH } catch (IOException e) { e.printStackTrace(); } });

Uso de parámetros de consulta

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

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

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

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

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

Cuando ejecuta una consulta sin una búsqueda indexada, invoca un análisis de tabla completo. En este ejemplo, recomendamos que tenga uníndiceen elGovIdpara optimizar el rendimiento. Sin índice enGovId, las consultas pueden tener más latencia y también pueden provocar excepciones de conflicto de OCC o tiempos de espera de transacción.

Inserción de documentos

En el siguiente ejemplo de código se insertan tipos de datos 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); } });

En el siguiente ejemplo de código se insertan tipos de datos 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(); } });

Esta transacción inserta un documento en elPersonmesa. Antes de insertar, comprueba primero si el documento ya existe en la tabla. Esta comprobación hace que la transacción sea idempotente por naturaleza. Incluso si ejecuta esta transacción varias veces, no causará efectos secundarios involuntarios.

nota

En este ejemplo, recomendamos tener un índice en laGovIdpara optimizar el rendimiento. Sin índice enGovId, las declaraciones pueden tener más latencia y también pueden dar lugar a excepciones de conflicto de OCC o tiempos de espera de transacción.

Inserción de varios documentos en una sola declaración

Para insertar varios documentos mediante un soloINSERT, puede pasar un parámetro de tipoIonList(emitido explícitamente comoIonValue) a la declaración de la siguiente manera.

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

No incluye el marcador de posición variable (?) en soportes de doble ángulo (<<...>>) al pasar unIonList. En las instrucciones manuales PartiQL, los corchetes angulares dobles indican una colección no ordenada conocida comobolsa.

LaTransactionExecutor.executeestá sobrecargado. Acepta un número variable deIonValueargumentos (varargs), o unoList<IonValue>argumento. Enion-java,IonListse implementa comoList<IonValue>.

Java tiene por defecto la implementación de métodos más específica cuando se llama a un método sobrecargado. En este caso, cuando pasas unIonList, se establece de forma predeterminada el método que toma unList<IonValue>. Cuando se invoca, la implementación de este método pasa elIonValueelementos de la lista como valores distintos. Por lo tanto, para invocar el método varargs en su lugar, debe emitir explícitamente unIonListparámetro comoIonValue.

Actualización de documentos

qldbDriver.execute(txn -> { final List<IonValue> parameters = new ArrayList<>(); parameters.add(SYSTEM.newString("John")); parameters.add(SYSTEM.newString("TOYENC486FH")); txn.execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", parameters); });
qldbDriver.execute(txn -> { try { final List<IonValue> parameters = new ArrayList<>(); parameters.add(MAPPER.writeValueAsIonValue("John")); parameters.add(MAPPER.writeValueAsIonValue("TOYENC486FH")); txn.execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", parameters); } catch (IOException e) { e.printStackTrace(); } });
nota

En este ejemplo, recomendamos que tenga un índice en laGovIdpara optimizar el rendimiento. Sin índice enGovId, las declaraciones pueden tener más latencia y también pueden dar lugar a excepciones de conflicto de OCC o tiempos de espera de transacción.

Eliminación de documentos

qldbDriver.execute(txn -> { txn.execute("DELETE FROM Person WHERE GovId = ?", SYSTEM.newString("TOYENC486FH")); });
qldbDriver.execute(txn -> { try { txn.execute("DELETE FROM Person WHERE GovId = ?", MAPPER.writeValueAsIonValue("TOYENC486FH")); } catch (IOException e) { e.printStackTrace(); } });
nota

En este ejemplo, recomendamos tener un índice en laGovIdpara optimizar el rendimiento. Sin índice enGovId, las declaraciones pueden tener más latencia y también pueden dar lugar a excepciones de conflicto de OCC o tiempos de espera de transacción.

Ejecución de varios estados de cuenta en una transacción

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

Lógica de reintento

El del conductorexecutetiene un mecanismo de reintento integrado que vuelve a intentar la transacción si se produce una excepción reintentable (como tiempos de espera o conflictos de OCC).

2.x

El número máximo de reintentos y la estrategia de retroceso son configurables.

El límite de reintentos predeterminado es4, y la estrategia de retroceso predeterminada esDefaultQldbTransactionBackoffStrategy. Puede establecer la configuración de reintento por instancia de controlador y también por transacción mediante una instancia deRetryPolicy.

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

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

En el siguiente ejemplo de código se especifica la lógica de reintentos con un límite de reintentos personalizado y una estrategia de retroceso personalizada para una transacción concreta. Esta configuración paraexecuteanula la lógica de reintento establecida para la instancia del controlador.

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

El número máximo de reintentos se puede configurar. Puede configurar el límite de reintento estableciendo laretryLimitpropiedad al inicializarPooledQldbDriver.

El límite de reintentos predeterminado es4.

Implementación de restricciones de exclusividad

QLDB no admite índices únicos. Pero es fácil implementar este comportamiento en tu aplicación.

Suponga que desea implementar una restricción de singularidad en laGovIden elPersonmesa. Para ello, puede escribir una transacción que realiza lo siguiente:

  1. Afirmar que la tabla no tiene documentos existentes con un determinadoGovId.

  2. Inserte el documento si se pasa la afirmación.

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

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

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

En este ejemplo, recomendamos que tenga un índice en laGovIdpara optimizar el rendimiento. Sin índice enGovId, las declaraciones pueden tener más latencia y también pueden dar lugar a excepciones de conflicto de OCC o tiempos de espera de transacción.

Trabajo con Amazon Ion

Existen varias formas de procesar los datos de Amazon Ion en QLDB. Puede utilizar métodos integrados desde elBiblioteca Ionpara crear y modificar documentos de forma flexible según sea necesario. O bien, puede utilizar lasMódulo de formato de datos Jackson para Ionpara asignar documentos de Ion aobjeto Java antiguomodelos (POJO).

En las secciones siguientes se proporcionan ejemplos de código del procesamiento de datos de iones utilizando ambas técnicas.

Importación de paquetes Ion

Añadir el artefactoion-javacomo dependencia de su proyecto 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>

Importe los siguientes paquetes de Ion.

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

Añadir el artefactojackson-dataformat-ioncomo dependencia de su proyecto Java. QLDB requiere versión2.10.0o posterior.

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>

Importe los siguientes paquetes de 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;

Inicialización Ion

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

Creación de objetos Ion

En el siguiente ejemplo de código se crea un objeto Ion mediante elIonStructinterfaz y sus métodos incorporados.

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

Suponga que tiene una clase de modelo asignada por JSON denominadaPerson, de la siguiente manera.

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

En el siguiente ejemplo de código se crea unIonStructobjeto de una instancia dePerson.

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

Lectura de objetos Ion

En el siguiente ejemplo de código se imprimen cada campo delionStructinstancia.

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

En el siguiente ejemplo de código se lee unIonStructobjeto y lo asigna a una instancia dePerson.

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

Para obtener más información acerca de cómo trabajar con Ion, consulte laDocumentación de Amazon Ionen GitHub. Para obtener más ejemplos de código sobre cómo trabajar con Ion en QLDB, consulteTrabajo con tipos de datos de Amazon Ion en Amazon QLDB.