Overview
Spring Boot es un framework Java de código abierto que simplifica el desarrollo de aplicaciones listas para producción. Al combinarse con Spring Data MongoDB, este framework proporciona una forma eficiente de interactuar con bases de datos MongoDB.
Este tutorial muestra cómo crear una aplicación reactiva de Spring Boot que utiliza Spring Data MongoDB para realizar operaciones en una base de datos MongoDB. Puede usar esta aplicación de saldo de caja de ejemplo para llamar a las API REST que realizan las siguientes acciones:
Crear o obtener una cuenta
Realizar transacciones en una cuenta o entre dos cuentas
Spring Data MongoDB
Spring Data MongoDB proporciona soporte para repositorios de MongoDB, ofreciendo dos maneras principales de interactuar con la base de datos:
ReactiveMongoRepository: una abstracción de nivel superior que proporciona acceso a las operaciones comunes de bases de datos.
ReactiveMongoTemplate: una abstracción de nivel inferior que ofrece mayor control sobre las operaciones de MongoDB y la construcción de consultas
Transacciones ACID multidocumento
MongoDB admite transacciones ACID multidocumento, que te permiten realizar múltiples operaciones que cumplen con las garantías de atomicidad, coherencia, aislamiento y durabilidad. Spring Data MongoDB ofrece soporte de funcionalidad incorporada para transacciones a través de la @Transactional anotación o la clase TransactionalOperator. La aplicación de muestra en este tutorial configura el soporte de transacciones para garantizar la atomicidad de las operaciones de la base de datos.
Para obtener más información sobre las transacciones de MongoDB, consulte Transacciones en el manual del servidor MongoDB.
Tutorial
Este tutorial muestra cómo descargar una aplicación de ejemplo que implementa un sistema simplificado de saldo de caja. La aplicación te permite crear, recuperar y actualizar el saldo de las cuentas bancarias. Para ver la aplicación completa, consulta el mdb-spring-boot-reactive repositorio de GitHub.
Complete el tutorial para aprender a realizar las siguientes acciones:
Descargar una aplicación de ejemplo de Spring Boot
Configura la aplicación para conectarse a MongoDB
Comprende la estructura de la aplicación y los componentes clave
Ejecutar la aplicación
Enviar solicitudes POST para crear cuentas bancarias y realizar transacciones
Descargue y configure su conexión
Verificar los prerrequisitos
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 21 o posterior.
Maven 3.5 o posterior.
Descargar la aplicación de ejemplo
Clone la aplicación de ejemplo del repositorio GitHub de MongoDB Developer ejecutando el siguiente comando:
git clone git@github.com:mongodb-developer/mdb-spring-boot-reactive.git
Este repositorio contiene una aplicación Spring Boot reactiva que utiliza Spring Data MongoDB para interactuar con una base de datos MongoDB.
Configura tu conexión de MongoDB
Navegue al archivo src/main/resources/application.properties en el directorio de la aplicación de ejemplo. En este archivo, configure la propiedad spring.data.mongodb.uri con la URI de conexión de Atlas, como se muestra en el siguiente código:
spring.data.mongodb.uri=<connection URI>
Reemplace el marcador de posición <connection URI> con su URI de conexión Atlas.
Habilitar la validación del esquema
Desde el directorio raíz, ejecuta el siguiente comando para configurar la validación de esquemas para tu aplicación:
mongosh "<connection URI>" --file setup.js
Este comando crea una restricción que garantiza que el saldo de la cuenta bancaria nunca esté por debajo de 0. Reemplaza el marcador de posición <connection URI> con tu URI de conexión de Atlas.
Entienda la estructura de la aplicación
Revisa los endpoints de gestión de cuentas
El archivo AccountController.java contiene los puntos finales de la API REST para crear y obtener cuentas. El archivo define un punto final del método POST que inserta un documento en la colección txn-demo.accounts y un punto final del método GET que encuentra un documento según su valor accountNum. El siguiente código muestra estos métodos:
public class AccountController { //... public Mono<Account> createAccount( Account account) { return accountRepository.save(account); } public Mono<Account> getAccount( String accountNum) { return accountRepository.findByAccountNum(accountNum) .switchIfEmpty(Mono.error(new AccountNotFoundException())); } //... }
Cada punto final devuelve un tipo Mono<Account> de AccountRepository, una interfaz ReactiveMongoRepository que actúa como una abstracción del controlador Java Reactive Streams subyacente.
Revisa los endpoints de transacciones de balance
La clase AccountController también incluye los siguientes puntos de acceso de transacciones:
Un endpoint
.../debitque agrega al saldo de una cuentaUn punto final
.../creditque descuenta del balance de una cuentaUn extremo
.../transferque realiza una transferencia de una cuenta a otra
Estos endpoints tienen las siguientes definiciones:
public class AccountController { //... public Mono<Txn> debitAccount( String accountNum, Map<String, Object> requestBody) { //... txn.addEntry(new TxnEntry(accountNum, amount)); return txnService.saveTransaction(txn).flatMap(txnService::executeTxn); } public Mono<Txn> creditAccount( String accountNum, Map<String, Object> requestBody) { //... txn.addEntry(new TxnEntry(accountNum, -amount)); return txnService.saveTransaction(txn).flatMap(txnService::executeTxn); } public Mono<Txn> transfer( String from, TransferRequest transferRequest) { //... txn.addEntry(new TxnEntry(from, -amount)); txn.addEntry(new TxnEntry(to, amount)); return txnService.saveTransaction(txn).flatMap(txnService::executeTxn); } //... }
Un objeto TxnEntry representa un cambio en una sola cuenta y un Txn puede consistir en uno o varios objetos TxnEntry. En el código de ejemplo, los endpoints de débito y crédito crean un nuevo TxnEntry objeto y el endpoint de transferencia crea dos TxnEntry objetos.
Revisa los métodos de query personalizada
La interfaz AccountRepository amplía ReactiveMongoRepository y define los siguientes métodos de consulta:
public interface AccountRepository extends ReactiveMongoRepository<Account, String> { Mono<Account> findByAccountNum(String accountNum); Mono<Long> findAndIncrementBalanceByAccountNum(String accountNum, double increment); }
El código utiliza las siguientes anotaciones que te permiten usar marcadores de posición al query MongoDB, los cuales se sustituyen dinámicamente con los argumentos del método:
@Query: Anota el métodofindByAccountNum()y especifica los criterios de consulta para encontrar documentos. El marcador?0se sustituye por el valor del parámetroaccountNum.@UpdateAnota el métodofindAndIncrementBalanceByAccountNum()y especifica la operación de actualización que se debe ejecutar en los documentos coincidentes. El marcador?1se sustituye por el valor del parámetroincrementpara aumentar el saldo utilizando el operador$incde MongoDB.
Nota
Aunque el punto final POST descrito en un paso anterior utiliza el accountRepository.save() método, no es necesario definirlo. El save() método y muchos otros métodos base ya están declarados por interfaces en la cadena de herencia ReactiveMongoRepository de.
Entender el servicio de transacciones
La clase TxnService gestiona la ejecución de transacciones. Esta clase incluye el siguiente código:
public class TxnService { //... public Mono<Txn> saveTransaction(Txn txn) { return txnTemplate.save(txn); } public Mono<Txn> executeTxn(Txn txn) { return updateBalances(txn) .onErrorResume(DataIntegrityViolationException.class /*lambda expression to handle error*/) .onErrorResume(AccountNotFoundException.class /*lambda expression to handle error*/) .then(txnTemplate.findAndUpdateStatusById(txn.getId(), TxnStatus.SUCCESS)); } public Flux<Long> updateBalances(Txn txn) { Flux<Long> updatedCounts = Flux.fromIterable(txn.getEntries()).concatMap( entry -> accountRepository.findAndIncrementBalanceByAccountNum( entry.getAccountNum(), entry.getAmount()) ); return updatedCounts.handle(/*...*/); } }
La clase TxnService incluye los siguientes métodos:
saveTransaction(): Guarda un documentoTxnen la coleccióntransactionsexecuteTxn(): Llama al métodoupdateBalances()y luego actualiza el estado de la transacción en el documentoTxnupdateBalances(): Itera a través de cadaTxnEntryy realiza las actualizaciones correspondientes de cada documentoaccount
Revisar la plantilla de transacción
Los métodos saveTransaction() y executeTxn() descritos en el paso anterior utilizan ambos métodos definidos en la clase TxnTemplate. Estos métodos tienen las siguientes definiciones:
public class TxnTemplate { //... public Mono<Txn> save(Txn txn) { return template.save(txn); } public Mono<Txn> findAndUpdateStatusById(String id, TxnStatus status) { Query query = query(where("_id").is(id)); Update update = update("status", status); FindAndModifyOptions options = FindAndModifyOptions.options().returnNew(true); return template.findAndModify(query, update, options, Txn.class); } //... }
Estos métodos interactúan con MongoDB usando el ReactiveMongoTemplate.
Configurar transacciones multidocumento
Al transferir dinero entre cuentas, puedes usar una transacción multi-documento. Las actualizaciones entre dos cuentas deben ser atómicas, y las transacciones garantizan la atomicidad de los datos.
Para acceder al soporte de transacciones de Spring, la aplicación de ejemplo añade el bean ReactiveMongoTransactionManager al archivo ReactiveMongoConfig.java:
public class ReactiveMongoConfig extends AbstractReactiveMongoConfiguration { //... ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory dbFactory) { return new ReactiveMongoTransactionManager(dbFactory); } }
Puede definir el alcance de una transacción mediante un objeto TransactionalOperator o la anotación @Transactional. La clase TxnService muestra ambos enfoques.
Tip
Transacciones de Spring Data
Para obtener más información sobre las transacciones y las sesiones en Spring Data MongoDB, consulta Sesiones y transacciones en la documentación de Spring Data.
Prueba la aplicación
Ejecutar la aplicación
Para iniciar la aplicación, ejecuta los siguientes comandos desde el directorio raíz del proyecto:
mvn spring-boot:run
Después de iniciar la aplicación, puedes acceder a ella en http://localhost:8080. Luego puede usar los endpoints de la API REST para crear cuentas y realizar transacciones.
Cree una cuenta
Para crear una nueva cuenta bancaria que tenga un número de cuenta de 12345, ejecuta el siguiente comando en tu shell:
curl --location 'localhost:8080/account' \ --header 'Content-Type: application/json' \ --data '{ "accountNum": "12345" }'
Este comando envía una publicación solicitud al endpoint /account para crear un nuevo documento de cuenta bancaria en la colección accounts.
Incrementa el saldo de tu cuenta
Para depositar dinero en la cuenta creada en el paso anterior, ejecute el siguiente comando en su Shell:
curl --location 'localhost:8080/account/12345/debit' \ --header 'Content-Type: application/json' \ --data '{ "amount": 1000 }'
Este comando envía una solicitud al endpoint .../debit para aumentar el valor de amount del documento account correspondiente en 1000.
Transferir dinero entre cuentas
Para transferir dinero de una cuenta a otra, crea una cuenta nueva con el número de cuenta 67890:
curl --location 'localhost:8080/account' \ --header 'Content-Type: application/json' \ --data '{ "accountNum": "67890" }'
Luego, transfiere 500 dólares a esta nueva cuenta ejecutando el siguiente comando:
curl --location 'localhost:8080/account/12345/transfer' \ --header 'Content-Type: application/json' \ --data '{ "to": "67890", "amount": 500 }'
Este comando envía una solicitud al endpoint .../transfer para completar la transferencia de saldo.
Recursos adicionales
Para obtener más información sobre Spring Data MongoDB, consulte la documentación de referencia de Spring Data MongoDB.
Para ver la aplicación de ejemplo completa, consulta el mdb-spring-boot-reactive repositorio de GitHub.