Overview
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.
Tutorial
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 coleccionescartyproduct, 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 coleccionescartyproduct.models/Cart.java: Clase POJO que representa un carrito de compras, correspondiente a un documento en la coleccióncart.models/Product.javaClase POJO que representa un ítem y sus acciones, correspondiente a un documento en la colecciónproduct.
Verificar los requisitos previos.
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.
Descarga la aplicación de muestra.
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.
Inicie un flujo de cambios y configure las colecciones.
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
carten la base de datostest.Crea la colección
producten la base de datostest.Aplica un JSON schema a la colección
productque establece restricciones de tipo de datos y valor en los campos del documento. Este esquema garantiza que el valor del campostockse mantenga por encima de0, 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.
Inicia las operaciones de datos insertando datos de muestra.
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) }
Ejecuta las primeras operaciones sin una transacción.
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) }
Ejecuta una segunda operación dentro de una transacción multidocumento.
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
cartque representa el carrito de AliceActualiza el valor de
items.quantitydel documento en2Actualiza el documento en la colección
productque 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) }
Ejecuta una operación fallida dentro de una transacción.
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.
Revisa el resultado del flujo de cambios.
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
productque representa cerveza.La primera compra de dos cervezas de Alice incluye dos operaciones: una para actualizar la colección
carty otra para actualizar la colecciónproduct. Estas operaciones no se ejecutan dentro de una transacción. Las operaciones tienen valoresTimestampdiferentes, 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
cartcomo laproduct. Estas operaciones tienen el mismo valorTimestampporque 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.
Información Adicional
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.