Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /

Tutorial: Ejecutar transacciones ACID multidocumento

En este tutorial, puedes aprender a ejecutar transacciones multidocumento que cumplen con las garantías de atomicidad, coherencia, aislamiento y durabilidad (ACID).

Todas las versiones de MongoDB Server admiten transacciones de un solo documento y garantizan ACID compliance cuando se realizan varias actualizaciones en un documento en una transacción. En MongoDB Server v4.0 y versiones posteriores, puedes ejecutar transacciones compatibles con ACID entre varios documentos, colecciones y bases de datos.

Este tutorial muestra cómo descargar un aplicación de ejemplo que ejecuta transacciones multi-documento sobre acciones de productos y datos de compras. El tutorial utiliza los siguientes archivos en la aplicación de ejemplo transactions directorio:

  • Transactions.javaAccede a las colecciones cart y product, y luego ejecuta operaciones en ambas colecciones para reflejar las compras de cerveza. El código opera dentro de una transacción ACID y sin una transacción para comparar los enfoques.

  • ChangeStreams.java: Devuelve información sobre cambios de datos en las colecciones cart y product.

  • models/Cart.java: Clase POJO que representa un carrito de compras, correspondiente a un documento en la colección cart.

  • models/Product.javaClase POJO que representa un ítem y sus acciones, correspondiente a un documento en la colección product.

1

Antes de empezar este tutorial, asegúrate de tener preparados los siguientes componentes:

  • Cuenta de MongoDB Atlas con un clúster configurado. Para aprender a crear un clúster, consulta la guía Primeros pasos con MongoDB.

  • Java driver v5.0 o posterior.

  • Java 21 o posterior.

  • Maven v3.8.7 o posterior.

2

Clona la aplicación de ejemplo del repositorio GitHub de MongoDB Developer ejecutando el siguiente comando en tu terminal:

git clone git@github.com:mongodb-developer/java-quick-start.git

Este repositorio contiene una carpeta transactions que almacena los archivos de este tutorial.

3

Para crear las colecciones cart y product y configurar un JSON schema, ejecuta el archivo ChangeStreams.java. Navegue al directorio java-quick-start y ejecute el siguiente comando:

mvn compile exec:java \
-Dexec.mainClass="com.mongodb.quickstart.transactions.ChangeStreams" \
-Dmongodb.uri="<connection URI>"

Tip

Reemplaza el marcador de posición <connection URI> con el URI de conexión de tu clúster.

Este archivo realiza las siguientes acciones:

  • Crea la colección cart en la base de datos test.

  • Crea la colección product en la base de datos test.

  • Aplica un JSON schema a la colección product que establece restricciones de tipo de datos y valor en los campos del documento. Este esquema garantiza que el valor del campo stock se mantenga por encima de 0, por lo que cualquier transacción que intente comprar un artículo agotado generará un error y no tendrá éxito.

  • Abre un flujo de cambios para supervisar los cambios en la base de datos test.

4

Alice es una cliente de muestra que quiere comprar cerveza. El archivo Transactions.java ejecuta operaciones de base de datos para reflejar sus compras. Para iniciar este programa, abre una segunda ventana de terminal y ejecuta el siguiente código desde el directorio raíz del proyecto:

mvn compile exec:java \
-Dexec.mainClass="com.mongodb.quickstart.transactions.Transactions" \
-Dmongodb.uri="<connection URI>"

Tip

Reemplaza el marcador de posición <connection URI> con el URI de conexión de tu clúster.

El archivo inserta un documento en la colección product que representa el inventario de cerveza y establece su valor stock en 5. Este documento almacena los siguientes datos:

{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(5) }
5

Después de insertar datos de muestra, el archivo Transactions.java ejecuta la primera operación de actualización para registrar la compra de dos cervezas por parte de Alice. El código llama a los métodos aliceWantsTwoBeers() y removingBeersFromStock() sin iniciar una transacción. Estos métodos tienen las siguientes definiciones:

private static void aliceWantsTwoBeers() {
System.out.println("Alice adds 2 beers in her cart.");
cartCollection.insertOne(new Cart("Alice", List.of(new Cart.Item(BEER_ID, 2, BEER_PRICE))));
}
private static void removingBeersFromStock() {
System.out.println("Trying to update beer stock : -2 beers.");
try {
productCollection.updateOne(filterId, decrementTwoBeers);
} catch (MongoException e) {
System.out.println("######## MongoException ########");
System.out.println("##### STOCK CANNOT BE NEGATIVE #####");
throw e;
}
}

El método aliceWantsTwoBeers() añade dos cervezas al carrito de compras de Alice insertando un documento en la colección cart que representa la compra. Luego, el método removingBeersFromStock() actualiza la colección product para reflejar cambios y reducir la cantidad de cervezas en stock.

Selecciona el Cart pestaña para ver el nuevo documento cart que representa el carrito de compras de Alicia y selecciona la pestaña Product para ver el documento product que representa el inventario de cervezas después de las operaciones:

{
"_id" : "Alice",
"items" : [
{
"price" : NumberDecimal("3"),
"productId" : "beer",
"quantity" : NumberInt(2)
}
]
}
{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(3) }
6

Después de la compra inicial, Alice añade dos cervezas adicionales a su carrito. El archivo Transactions.java utiliza una transacción para ejecutar esta segunda operación llamando al método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() y pasando un MongoClient como argumento. Este método tiene la siguiente definición:

private static void aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback(MongoClient client) {
ClientSession session = client.startSession();
try {
session.startTransaction(TransactionOptions.builder().writeConcern(WriteConcern.MAJORITY).build());
aliceWantsTwoExtraBeers(session);
sleep();
removingBeerFromStock(session);
session.commitTransaction();
} catch (MongoException e) {
session.abortTransaction();
System.out.println("####### ROLLBACK TRANSACTION #######");
} finally {
session.close();
System.out.println("####################################\n");
printDatabaseState();
}
}

El método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() inicia una sesión y, luego, inicia una transacción. Dentro de la transacción, el código llama a métodos auxiliares para realizar las siguientes acciones:

  • Encuentra el documento en la colección cart que representa el carrito de Alice

  • Actualiza el valor de items.quantity del documento en 2

  • Actualiza el documento en la colección product que representa las acciones de cerveza para reflejar el cambio

Dado que se ejecutan dentro de una ACID transaction multidocumento, las actualizaciones cart y product son atómicas.

Selecciona la pestaña Cart para ver el documento actualizado cart que representa el carrito de compras de Alice y selecciona la pestaña Product para ver el documento actualizado product que representa el inventario de cerveza:

{
"_id" : "Alice",
"items" : [
{
"price" : NumberDecimal("3"),
"productId" : "beer",
"quantity" : NumberInt(4)
}
]
}
{ "_id" : "beer", "price" : NumberDecimal("3"), "stock" : NumberInt(1) }
7

Finalmente, Alice intenta añadir dos cervezas más a su carrito. El archivo Transactions.java utiliza una transacción para ejecutar esta tercera operación llamando nuevamente al método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback().

Sin embargo, esta operación no tiene éxito porque solo queda una cerveza en existencias. El JSON schema configurado en el archivo ChangeStreams.java garantiza que el valor de stock en la colección product no pueda estar por debajo de 0, por lo que el intento de restar 2 de su valor actual arroja un error. El método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() revierte la transacción.

8

Después de que Transactions.java termine de ejecutarse, la salida del archivo ChangeStreams.java se parecerá a lo siguiente:

Dropping the 'test' database.
Creating the 'cart' collection.
Creating the 'product' collection with a JSON Schema.
Watching the collections in the DB test...
Timestamp{value=7304460075832180737, seconds=1700702141, inc=1} => Document{{_id=beer, price=3, stock=5}}
Timestamp{value=7304460075832180738, seconds=1700702141, inc=2} => Document{{_id=Alice, items=[Document{{price=3, productId=beer, quantity=2}}]}}
Timestamp{value=7304460080127148033, seconds=1700702142, inc=1} => Document{{_id=beer, price=3, stock=3}}
Timestamp{value=7304460088717082625, seconds=1700702144, inc=1} => Document{{_id=Alice, items=[Document{{price=3, productId=beer, quantity=4}}]}}
Timestamp{value=7304460088717082625, seconds=1700702144, inc=1} => Document{{_id=beer, price=3, stock=1}}

El flujo de cambios imprime información sobre la configuración de la colección y las siguientes operaciones:

  • La operación de inserción, que agrega un documento a la colección product que representa cerveza.

  • La primera compra de dos cervezas de Alice incluye dos operaciones: una para actualizar la colección cart y otra para actualizar la colección product. Estas operaciones no se ejecutan dentro de una transacción. Las operaciones tienen valores Timestamp diferentes, ya que no se ejecutan de forma atómica.

  • La próxima compra de Alice de dos cervezas, que también actualiza tanto la colección cart como la product. Estas operaciones tienen el mismo valor Timestamp porque se ejecutan de forma atómica en una transacción multi-documento.

Después de completar este tutorial, tendrás una aplicación que actualiza los datos de gestión de acciones. La aplicación realiza estas operaciones de actualización con y sin una ACID transaction multidocumento para comparar los dos resultados.

Para ver la aplicación de ejemplo completa, consulta la carpeta de transacciones en el repositorio de GitHub de java-quick-start.

Para obtener más información sobre las transacciones, consulta la guía de Transacciones.

Volver

Transacciones