Spring Reactive Multi MongoTemplate Configuration

Visweshwar Ganesh
3 min readNov 27, 2020

--

Configuring your application with Spring Data and MongoDB is quite well documented on the internet. Be it the Spring Data documentation or the great starter projects from baeldung.com.

What I was missing was a reference example for a multi mongoTemplate configuration.

Why Multi MongoTemplate Configuration?

There are instances where your application interacts with more than just one instance of a mongoDB database. When you get into this state, the out of the box configurations fail to recognize this state.

What do we need to solve?

There are a few unique issues which you’d experience when you encounter this condition.

  • How do we define our Database Properties?
  • How do we does the repository differentiate which Database to interact with?
  • How do we configure DB options?

Let’s go about answering these questions using a very simple command line example.

Defining MongoDB Properties

To define the properties in this case we break away from the “traditional” spring parameters.

Here is a sample Configuration.

application.yml

spring:
data:
mongodb:
coffee:
database: coffee
uri: mongodb://localhost:27017
burger:
database: burger
uri: mongodb://localhost:27017
autoconfigure:
exclude: org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration

We have two databases coffee and burger these could represent the same replicaset or different replicaset. In this case they refer to the same replicaset.

Similar to the Non Reactive Mongo Autoconfiguration, we have exclude the autoconfiguration as we are going to take it over.

Reading the Config file

There is nothing unusual here, this is the general way we reach the property files. Note the ConfigurationProperty Prefix.

package com.acme.multimongo.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "spring.data.mongodb")
@Getter
@Setter
public class CustomMongoProperties {
private MongoProperties coffee;
private MongoProperties burger;

}

Define The Configuration

To define the configuration there are 2 parts

  • MongoConfig
  • MongoTemplate
  • MongoClient

Defining the Mongo Config

Now we have to define a Config for each Database

package com.acme.multimongo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;


@Configuration
@EnableReactiveMongoRepositories(basePackages = "com.acme.multimongo.repository.burger",
reactiveMongoTemplateRef = "mongoTemplateBurger")

public class BurgerMongoConfig {
}

NOTE: We have Separated the Annotation @EnableReactiveMongoRepositories by defining basePackage and a reactiveMongoTemplateRef (We’ll see the reactiveMongoTemplateRef in the next section)

package com.acme.multimongo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;


@Configuration
@EnableReactiveMongoRepositories(basePackages = "com.acme.multimongo.repository.coffee",
reactiveMongoTemplateRef = "mongoTemplateCoffee")

public class CoffeeMongoConfig {
}

Defining MongoClient and MongoTemplates

Let’s define the configuration for the Coffee Client

As you can see below we have defined the MongoClient, where we inject the ClientSettings and then inject the MongoClient into the template.

NOTE: We have to define any one of the MongoTemplate as @Primary.

@Primary
@Bean
public MongoClient reactiveMongoClientCoffee() {
return MongoClients.create(createMongoClientSettings(customMongoProperties.getCoffee()));
}
@Primary
@Bean("mongoTemplateCoffee")
public ReactiveMongoTemplate reactiveMongoTemplateCoffee(){
return new ReactiveMongoTemplate(reactiveMongoClientCoffee(),customMongoProperties.getCoffee().getDatabase());
}

private MongoClientSettings createMongoClientSettings(MongoProperties mongoProperties){

ConnectionString ConnectionString = new ConnectionString(mongoProperties.getUri());

MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.readConcern(ReadConcern.DEFAULT)
.writeConcern(WriteConcern.MAJORITY)
.readPreference(ReadPreference.primary())
.applyConnectionString(ConnectionString)
.build();
return mongoClientSettings;
}

Similarly for the burger client. Putting it now all together

package com.acme.multimongo.config;

import com.mongodb.*;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;

@Configuration
public class MongoConfiguration {

private final CustomMongoProperties customMongoProperties;

public MongoConfiguration(CustomMongoProperties customMongoProperties) {
this.customMongoProperties = customMongoProperties;
}

@Primary
@Bean
public MongoClient reactiveMongoClientCoffee() {
return MongoClients.create(createMongoClientSettings(customMongoProperties.getCoffee()));
}

@Bean
public MongoClient reactiveMongoClientBurger() {
return MongoClients.create(createMongoClientSettings(customMongoProperties.getBurger()));
}

@Primary
@Bean("mongoTemplateCoffee")
public ReactiveMongoTemplate reactiveMongoTemplateCoffee(){
return new ReactiveMongoTemplate(reactiveMongoClientCoffee(),customMongoProperties.getCoffee().getDatabase());
}
@Bean("mongoTemplateBurger")
public ReactiveMongoTemplate reactiveMongoTemplateBurger(){
return new ReactiveMongoTemplate(reactiveMongoClientBurger(),customMongoProperties.getBurger().getDatabase());
}


private MongoClientSettings createMongoClientSettings(MongoProperties mongoProperties){

ConnectionString ConnectionString = new ConnectionString(mongoProperties.getUri());

MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.readConcern(ReadConcern.DEFAULT)
.writeConcern(WriteConcern.MAJORITY)
.readPreference(ReadPreference.primary())
.applyConnectionString(ConnectionString)
.build();
return mongoClientSettings;
}
}

Using these Configuration in a simple class

Details on how MongoDB and Reactive work are beyond the scope of this document. It’s worth referring to documentation for that purpose.

package com.acme.multimongo;

import com.acme.multimongo.repository.burger.BurgerStoreRepository;
import com.acme.multimongo.repository.burger.StoreEntity;
import com.acme.multimongo.repository.coffee.CoffeeShopRepository;
import com.acme.multimongo.repository.coffee.ShopEntity;
import java.util.List;
import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.reactive.config.EnableWebFlux;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@SpringBootApplication
@EnableWebFlux
public class MultimongoApplication implements CommandLineRunner {

@Autowired
private BurgerStoreRepository burgerStoreRepository;
@Autowired
private CoffeeShopRepository coffeeShopRepository;



public static void main(String[] args) {
SpringApplication.run(MultimongoApplication.class, args);
}

@Override
public void run(String ... args) {
Mono<List<StoreEntity>> storeList = this.testStore();
Mono<List<ShopEntity>> cafeList = this.testCafe();

storeList.then(cafeList).subscribe();
}

protected Mono<List<StoreEntity>> testStore(){
Mono storeDeleteMono = this.burgerStoreRepository.deleteAll();


Mono<List<StoreEntity>> rsp = storeDeleteMono
.thenMany(
Flux.just("BK","MCD","FG")
.map(name -> new StoreEntity(name,new Random().nextInt()%10))
.flatMap(burgerStoreRepository::save)
).thenMany(burgerStoreRepository.findAll())
.collectList();

return rsp;


}
protected Mono<List<ShopEntity>> testCafe(){
Mono shopDeleteMono = this.coffeeShopRepository.deleteAll();


return shopDeleteMono
.thenMany(
Flux.just("SB","DD","TH")
.map(name -> new ShopEntity(name,new Random().nextInt()%10))
.flatMap(coffeeShopRepository::save)
).thenMany(coffeeShopRepository.findAll())
.collectList();
}


}

As always the code is available on Github

References

Spring Data Documentation for MongoDB — https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#reference

Spring Reactive Configuration for MongoDB — https://www.baeldung.com/spring-data-mongodb-reactive

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Visweshwar Ganesh
Visweshwar Ganesh

Written by Visweshwar Ganesh

Developer, Solution Lead @Paychex. Always look for opportunities to innovate and explore. Twitter: @visweshwar.LinkedIn: visweshwar-ganesh-5797405

No responses yet

Write a response