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 del libro de cocina
Esta guía de referencia muestra los casos de uso más comunes del controlador Amazon QLDB para Java. En este ejemplo de código Java se muestra cómo usar 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 mejores prácticas para hacer que las transacciones sean idempotentes e implementar restricciones de exclusividad.
nota
Cuando corresponda, algunos casos de uso tienen diferentes ejemplos de código para cada versión principal compatible del controlador QLDB para Java.
Contenido
Importación del controlador
El siguiente ejemplo de código importa el controlador, el cliente de sesión QLDB, los paquetes Amazon Ion y otras dependencias relacionadas.
Crear una instancia del controlador
El siguiente ejemplo de código crea una instancia de controlador que se conecta a un nombre de registro especificado y utiliza la lógica de reintentos especificada con un límite de reintentos personalizado.
nota
En este ejemplo también se crea una instancia de un objeto del sistema Amazon Ion (IonSystem
). Necesita este objeto para procesar los datos de Ion al ejecutar algunas operaciones de datos en esta referencia. Para obtener más información, consulte Trabajo con Ion.
Operaciones CRUD
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 transacciones idempotentes
Le recomendamos que haga que las transacciones de escritura sean idempotentes para evitar efectos secundarios inesperados en caso de reintentos. Una transacción es idempotente si 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
. 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 lado del servidor, pero el cliente agota el tiempo de espera mientras espera una respuesta. Si la transacción no es idempotente, podría insertarse el mismo documento más de una vez en caso de reintentarlo.
Uso de índices para evitar escaneos completos de tablas
También se recomienda ejecutar sentencias con una cláusula deWHERE
predicado mediante un operador de igualdad en un campo indexado o un identificador de documento; por ejemplo,WHERE indexedField = 123
oWHERE indexedField IN (456, 789)
. Sin esta búsqueda indexada, QLDB necesita realizar un escaneo de tablas, lo que puede provocar tiempos de espera de las transacciones o conflictos optimistas en el control de concurrencia (OCC).
Para obtener más información acerca de OCC, consulteModelo de simultaneidad de Amazon QLDB.
Transacciones creadas implícitamente
El método QldbDriver.executeExecutor
instancia envuelve una transacción creada de forma implícita.
Puede ejecutar sentencias dentro de la función lambda mediante elExecutor.execute
método. El controlador confirma implícitamente la transacción cuando la función lambda retorna.
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.
nota
Cuando corresponda, estas secciones proporcionan ejemplos de código sobre el procesamiento de datos de Amazon Ion mediante la biblioteca Ion integrada y la biblioteca de mapeadores Jackson Ion. Para obtener más información, consulte Trabajo con Ion.
Contenido
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
El siguiente ejemplo de código usa 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 });
El siguiente ejemplo de código utiliza 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 });
El siguiente ejemplo de código usa 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
El siguiente ejemplo de código usa 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(); } });
El siguiente ejemplo de código utiliza 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(); } });
El siguiente ejemplo de código usa 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 se ejecuta una consulta sin una búsqueda indexada, se invoca un análisis completo de la tabla. En este ejemplo, se recomienda tener un índice en elGovId
campo para optimizar el rendimiento. Si no hay un índice activadoGovId
, las consultas pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
Inserción de documentos
El siguiente ejemplo de código inserta tipos de datos de iones.
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); } });
El siguiente ejemplo de código inserta tipos de datos de iones.
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 laPerson
tabla. Antes de insertarlo, primero comprueba si el documento ya existe en la tabla. Esta comprobación hace que la transacción sea de naturaleza idempotente. Incluso si realizas esta transacción varias veces, no causará ningún efecto secundario no deseado.
nota
En este ejemplo, se recomienda tener un índice en elGovId
campo para optimizar el rendimiento. Sin un índice activadoGovId
, las declaraciones pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
Insertar varios documentos en una declaración
Para insertar varios documentos mediante una solaINSERT sentencia, puede pasar un parámetro de tipo IonList(expresado explícitamente como unIonValue
) a la sentencia de la siguiente manera.
// people is an IonList explicitly cast as an IonValue txn.execute("INSERT INTO People ?", (IonValue) people);
No se incluye el marcador de posición variable (?
) entre corchetes de doble ángulo (<<...>>
) al pasar unIonList
. En las sentencias PartiQL manuales, los corchetes de doble ángulo indican una colección desordenada conocida como bolsa.
El método TransactionExecutor.executeIonValue
argumentos (varargs) o un soloList<IonValue>
argumento. En ion-javaIonList
se implementa comoList<IonValue>
.
Java utiliza de forma predeterminada la implementación del método más específica cuando llamas a un método sobrecargado. En este caso, al pasar unIonList
parámetro, el valor predeterminado es el método que toma unList<IonValue>
. Cuando se invoca, la implementación de este método pasa losIonValue
elementos de la lista como valores distintos. Por lo tanto, para invocar el método varargs en su lugar, debe convertir explícitamente unIonList
pará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, se recomienda tener un índice en elGovId
campo para optimizar el rendimiento. Sin un índice activadoGovId
, las declaraciones pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
Eliminar 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, se recomienda tener un índice en elGovId
campo para optimizar el rendimiento. Sin un índice activadoGovId
, las declaraciones pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
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; }); }
Reintentar la lógica
Elexecute
método del controlador 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).
Implementación de restricciones de unicidad
QLDB no admite índices únicos, pero puede implementar este comportamiento en su aplicación.
Supongamos que desea implementar una restricción de exclusividad en elGovId
campo de laPerson
tabla. Para ello, puede escribir una transacción que haga lo siguiente:
-
Afirme que la tabla no tiene ningún documento existente con un valor especificado
GovId
. -
Inserte el documento si se aprueba la afirmación.
Si una transacción competidora aprueba simultáneamente la afirmación, solo una de las transacciones se confirmará correctamente. La otra transacción fallará si se produce 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 unicidad.
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, se recomienda tener un índice en elGovId
campo para optimizar el rendimiento. Sin un índice activadoGovId
, las declaraciones pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
Trabajo con Ion
Hay varias formas de procesar datos de Amazon Ion en QLDB. Puede utilizar los métodos integrados de la biblioteca Ion
Las siguientes secciones proporcionan ejemplos de código del procesamiento de datos de iones mediante ambas técnicas.
Contenido
Importación de los paquetes de iones
Agregue el artefacto ion-java
Importe los siguientes paquetes de iones.
import com.amazon.ion.IonStruct; import com.amazon.ion.IonSystem; import com.amazon.ion.system.IonSystemBuilder;
Agregue el artefacto jackson-dataformat-ion2.10.0
o posterior.
Importe los siguientes paquetes de iones.
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;
Ion de inicialización
IonSystem SYSTEM = IonSystemBuilder.standard().build();
IonObjectMapper MAPPER = new IonValueMapper(IonSystemBuilder.standard().build());
Creación de objetos de Ion
El siguiente ejemplo de código crea un objeto Ion mediante laIonStruct
interfaz y sus métodos integrados.
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
Supongamos que tiene una clase de modelo mapeada en 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; } }
El siguiente ejemplo de código crea unIonStruct
objeto a partir de una instancia dePerson
.
IonStruct ionStruct = (IonStruct) MAPPER.writeValueAsIonValue(new Person("Brent", "TOYENC486FH"));
Lectura de Ion
El siguiente ejemplo de código imprime cada campo de laionStruct
instancia.
// ionStruct is an instance of IonStruct System.out.println(ionStruct.get("GovId")); // prints TOYENC486FH System.out.println(ionStruct.get("FirstName")); // prints Brent
El siguiente ejemplo de código lee unIonStruct
objeto 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 sobre cómo trabajar con Ion, consulte la documentación de Amazon Ion