BlogAnnounced at MongoDB.local NYC 2024: A recap of all announcements and updatesLearn more >>
MongoDB Developer
Java
plus
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right
Languageschevron-right
Javachevron-right

Reactive Java Spring Boot with MongoDB

Teo Wen Jie5 min read • Published Apr 02, 2024 • Updated Apr 02, 2024
SpringMongoDBJava
Facebook Icontwitter iconlinkedin icon
Rate this code example
star-empty
star-empty
star-empty
star-empty
star-empty

Introduction

Spring Boot + Reactive + Spring Data + MongoDB. Putting these four technologies together can be a challenge, especially if you are just starting out. Without getting into details of each of these technologies, this tutorial aims to help you get a jump start on a working code base based on this technology stack. This tutorial features:
  • Interacting with MongoDB using ReactiveMongoRepositories.
  • Interacting with MongoDB using ReactiveMongoTemplate.
  • Wrapping queries in a multi-document ACID transaction.
This simplified cash balance application allows you to make REST API calls to:
  • Create or fetch an account.
  • Perform transactions on one account or between two accounts.

GitHub repository

Access the repository README for more details on the functional specifications. The README also contains setup, API usage, and testing instructions. To clone the repository:

Code walkthrough

Let's do a logical walkthrough of how the code works. I would include code snippets, but to reduce verbosity, I will exclude lines of code that are not key to our understanding of how the code works.

Creating or fetching an account

This section showcases how you can perform Create and Read operations with ReactiveMongoRepository.
The API endpoints to create or fetch an account can be found in AccountController.java:
This snippet shows two endpoints:
  • A POST method endpoint that creates an account
  • A GET method endpoint that retrieves an account but throws an exception if it cannot be found
They both simply return a Mono<Account> from AccountRepository.java, a ReactiveMongoRespository interface which acts as an abstraction from the underlying Reactive Streams Driver.
  • .save(...) method creates a new document in the accounts collection in our MongoDB database.
  • .findByAccountNum() method fetches a document that matches the accountNum.
The @Query annotation allows you to specify a MongoDB query with placeholders so that it can be dynamically substituted with values from method arguments. ?0 would be substituted by the value of the first method argument and ?1 would be substituted by the second, and so on and so forth.
The built-in query builder mechanism can actually determine the intended query based on the method's name. In this case, we could actually exclude the @Query annotation but I left it there for better clarity and to illustrate the previous point.
Notice that there is no need to declare a save(...) method even though we are actually using accountRepository.save() in AccountController.java. The save(...) method, and many other base methods, are already declared by interfaces up in the inheritance chain of ReactiveMongoRepository.

Debit, credit, and transfer

This section showcases:
  • Update operations with ReactiveMongoRepository.
  • Create, Read, and Update operations with ReactiveMongoTemplate.
Back to AccountController.java:
This snippet shows three endpoints:
  • A .../debit endpoint that adds to an account balance
  • A .../credit endpoint that subtracts from an account balance
  • A .../transfer endpoint that performs a transfer from one account to another
Notice that all three methods look really similar. The main idea is:
  • A Txn can consist of one to many TxnEntry.
  • A TxnEntry is a reflection of a change we are about to make to a single account.
  • A debit or credit Txn will only have one TxnEntry.
  • A transfer Txn will have two TxnEntry.
  • In all three operations, we first save one record of the Txn we are about to perform, and then make the intended changes to the target accounts using the TxnService.java.
The updateBalances(...) method is responsible for iterating through each TxnEntry and making the corresponding updates to each account. This is done by calling the findAndIncrementBalanceByAccountNum(...) method in AccountRespository.java.
Similar to declaring find methods, you can also declare Data Manipulation Methods in the ReactiveMongoRepository, such as update methods. Once again, the query builder mechanism is able to determine that we are interested in querying by accountNum based on the naming of the method, and we define the action of an update using the @Update annotation. In this case, the action is an $inc and notice that we used ?1 as a placeholder because we want to substitute it with the value of the second argument of the method.
Moving on, in TxnService we also have:
  • A saveTransaction method that saves a Txn document into transactions collection.
  • A executeTxn method that calls updateBalances(...) and then updates the transaction status in the Txn document created.
Both utilize the TxnTemplate that contains a ReactiveMongoTemplate.
The ReactiveMongoTemplate provides us with more customizable ways to interact with MongoDB and is a thinner layer of abstraction compared to ReactiveMongoRepository.
In the findAndUpdateStatusById(...) method, we are pretty much defining the query logic by code, but we are also able to specify that the update should return the newly updated document.

Multi-document ACID transactions

The transfer feature in this application is a perfect use case for multi-document transactions because the updates across two accounts need to be atomic.
In order for the application to gain access to Spring's transaction support, we first need to add a ReactiveMongoTransactionManager bean to our configuration as such:
With this, we can proceed to define the scope of our transactions. We will showcase two methods:
1. Using TransactionalOperator
The ReactiveMongoTransactionManager provides us with a TransactionOperator.
We can then define the scope of a transaction by appending .as(transactionalOperator::transactional) to the method call.
2. Using @Transactional annotation
We can also simply define the scope of our transaction by annotating the method with the @Transactional annotation.
Read more about transactions and sessions in Spring Data MongoDB for more information.

Conclusion

We are done! I hope this post was helpful for you in one way or another. If you have any questions, visit the MongoDB Community, where MongoDB engineers and the community can help you with your next big idea!
Once again, you may access the code from the GitHub repository, and if you are just getting started, it may be worth bookmarking Spring Data MongoDB.

Facebook Icontwitter iconlinkedin icon
Rate this code example
star-empty
star-empty
star-empty
star-empty
star-empty
Related
Tutorial

Schema Performance Evaluation in MongoDB Using PerformanceBench


Apr 02, 2024 | 20 min read
Article

MongoDB ORMs, ODMs, and Libraries


Apr 02, 2024 | 3 min read
Tutorial

Microservices Architecture With Java, Spring, and MongoDB


Apr 17, 2024 | 4 min read
Tutorial

Secure your API with Spring Data MongoDB and Microsoft EntraID


Apr 02, 2024 | 8 min read
Technologies Used
Languages
Technologies
Products
Table of Contents
  • Introduction