Introduction
In this blog post we will use ReactiveMongoTransactionManager class to manage the transaction. Along with that we shall use the mongo db.
Version
Dependencies
You can use Spring Initializer to initialize the project you need following dependencies -
Create docker-compose.yml
file
I use docker for running the mongo db the docker compose file looks like this -
version: '3.1'
services:
mongo:
container_name: spring-reactive
image: mongo:4.2.0-rc2-bionic
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
Configure MongoDb to the application
Add the following configurartion in the application.yml
file.
spring:
data:
mongodb:
uri: mongodb://root:example@localhost:27017
database: customer
authentication-database: admin
Create a User document
Create a User class that would have an age
field. You shall not allow the user to create an account if the age
of the user is below 18
.
@Document
data class User(
@Id
val id: String,
val age: Int
)
Create a data repository for User document
interface CustomerRepo : ReactiveCrudRepository<User, String>
Create a service class
Now create a service class that would have a method called saveAll
. The saveAll
method will accept the list of an Int array, create the user object, validate the age and the save data to the database.
fun saveAll(vararg ages: Int): Flux<User> {
return Flux.fromIterable(ages.asIterable())
.map {
User(id = UUID.randomUUID().toString(),
age = it)
}
.doOnNext { Assert.isTrue(it.age >= 18, "Age should be greater or equal to 18") }
.flatMap { userRepo.save(it) }
}
Make the service transactional
To add the transactional support to the application, provide two beans transactionManager
and transactionOperator
to enable the transactions.
Create a transaction manager
Create a ReactiveTransactionManager
that will manages the client sessions for the given ReactiveMongoDatabaseFactory
@Bean
fun transactionManager(rdbf: ReactiveMongoDatabaseFactory): ReactiveMongoTransactionManager {
return ReactiveMongoTransactionManager(rdbf)
}
Create a transaction operator
Operator class that simplifies programmatic transaction demarcation and transaction exception handling. Create a new TransactionalOperator
using ReactiveTransactionManager
, using a default transaction.
@Bean
fun transactionOperator(rtm: ReactiveTransactionManager): TransactionalOperator {
return TransactionalOperator.create(rtm)
}
Add transaction to the service
You can add the transaction by wrapping the callback in the execute
method of transactionOperator
like below -
val data = Flux.fromIterable(ages.asIterable())
.map {
User(id = UUID.randomUUID().toString(),
age = it)
}
.doOnNext { Assert.isTrue(it.age >= 18, "Age should be greater or equal to 18") }
.flatMap { userRepo.save(it) }
return transactionalOperator.execute{ data}
OR
You can use annotation @Transactional
at the method, which you want to make transactional
@Transactional
fun saveAll(vararg ages: Int): Flux<User> {
Source Code Full source is available at GitHub