Docs Menu
Docs Home
/ /

Tutorial: Ejecutar transacciones ACID de múltiples documentos

En este tutorial, puede aprender a ejecutar transacciones de múltiples documentos que cumplan con las garantías de atomicidad, consistencia, aislamiento y durabilidad (ACID).

Todas las versiones de MongoDB Server admiten transacciones de un solo documento y garantizan la conformidad con ACID al realizar múltiples actualizaciones a un documento en una transacción. En MongoDB Server v4.0 y versiones posteriores, puede ejecutar transacciones compatibles con ACID en múltiples documentos, colecciones y bases de datos.

Este tutorial muestra cómo descargar un aplicación de ejemplo que ejecuta transacciones multidocumento sobre el stock de productos y los 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, a continuación, ejecuta operaciones en ambas colecciones para reflejar las compras de cerveza. El código opera dentro de una transacción ACID y sin ella para comparar los enfoques.

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

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

  • models/Product.java:Clase POJO que representa un artículo y su stock, correspondiente a un documento de la colección product.

1

Antes de comenzar este tutorial, asegúrese de tener los siguientes componentes preparados:

  • Cuenta de MongoDB Atlas con un clúster configurado. Para aprender a crear un clúster, consulte la guía de introducción a MongoDB.

  • Controlador Java v5.0 o posterior.

  • Java 21 o posterior.

  • Maven v3.8.7 o posterior.

2

Clone la aplicación de muestra del repositorio GitHub de MongoDB Developer ejecutando el siguiente comando en su terminal:

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

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

3

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

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

Tip

Reemplace el marcador de posición <connection URI> con la URI de conexión de su 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 esquema JSON a la colección product que establece restricciones de tipo y valor de datos en los campos del documento. Este esquema garantiza que el valor del campo stock se mantenga por encima de 0, de modo que cualquier transacción que intente comprar un artículo agotado genere un error y no se complete.

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

4

Alice es una clienta de ejemplo que quiere comprar cerveza. El archivo Transactions.java ejecuta operaciones de base de datos para reflejar sus compras. Para iniciar este programa, abra una segunda ventana de terminal y ejecute 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

Reemplace el marcador de posición <connection URI> con la URI de conexión de su 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

Tras insertar datos de muestra, el archivo Transactions.java ejecuta la primera operación de actualización para representar la compra de dos cervezas por parte de Alice. El código invoca 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 los cambios y reducir el número de cervezas en stock.

Seleccione el Cart pestaña para ver el nuevo documento cart que representa el carrito de compras de Alice y seleccione la pestaña Product para ver el documento product que representa el inventario de cerveza 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

Tras la compra inicial, Alice añade dos cervezas más 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, a continuación, una transacción. Dentro de la transacción, el código invoca métodos auxiliares para realizar las siguientes acciones:

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

  • Actualizar el valor items.quantity del documento por 2

  • Actualice el documento en la colección product que representa el stock de cerveza para reflejar el cambio

Dado que se ejecutan dentro de una transacción ACID de múltiples documentos, las actualizaciones cart y product son atómicas.

Seleccione la pestaña Cart para ver el documento cart actualizado que representa el carrito de compras de Alice, y seleccione la pestaña Product para ver el documento product actualizado 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, Alicia 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 de nuevo al método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback().

Sin embargo, esta operación no se realiza correctamente porque solo queda una cerveza en stock. El esquema JSON configurado en el archivo ChangeStreams.java garantiza que el valor stock de la colección product no pueda ser inferior a 0, por lo que intentar restar 2 de su valor actual genera un error. El método aliceWantsTwoExtraBeersInTransactionThenCommitOrRollback() revierte la transacción.

8

Una vez que Transactions.java termina de ejecutarse, la salida del archivo ChangeStreams.java se parece 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 la cerveza.

  • La primera compra de Alice de dos cervezas 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. Tienen valores Timestamp diferentes, ya que no se ejecutan de forma automática.

  • La siguiente compra de Alice de dos cervezas también actualiza las colecciones cart y product. Estas operaciones tienen el mismo valor Timestamp porque se ejecutan automáticamente en una transacción multidocumento.

Tras completar este tutorial, dispondrá de una aplicación que actualiza los datos de gestión de inventario. La aplicación realiza estas operaciones de actualización con y sin una transacción ACID multidocumento para comparar ambos resultados.

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

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

Volver

Transacciones