I have a SpringBoot application that currently uses autoconfiguration to connect to MongoDB. I need to add more MongoDB datasources.
I tried to manually create them but I'm struggling to find the right configuration.
I've found Spring: 2 Repositories out of a single Entity and Multiple MongoDB connectors with Spring Boot but the underlying classes are outdated and it's throwing the error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Timeout on blocking read for 5000000000 NANOSECONDS
CodePudding user response:
Spring Boot autoconfig works great for one data source but doesn't support multiple connectors.
To do that we have to manually create the Repository instance, but because they are dynamically implemented from an interface is not possible to create the concrete instance and add parameters.
Steps:
- Disable Spring Auto config for Mongo so we can provide the beans manually
- Create a
ReactiveMongoTemplate
bean for each connector needed. This will be used by Spring to create newReactiveRepositories
instances - Create the
ReactiveRepository
instances in different sibling packages. - Finally create a
@Configuration
for each connector needed and specify the package where theReactiveRepository
instance resides (see example below)
//# 1. Disable autoconfig
@SpringBootApplication(exclude = {
MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class
})
public class XyzApplication {
...
}
//# 2. Create reactive template beans
package com.xyz.config;
...
@Configuration
public class MultipleMongoConfig {
@Value("${mongodb.primary.uri}")
private String mongodbPrimaryUri;
@Value("${mongodb.readonly.uri}")
private String mongodbSecondaryUri;
@Primary
@Bean
public ReactiveMongoTemplate primaryMongoTemplate() {
return new ReactiveMongoTemplate(
new SimpleReactiveMongoDatabaseFactory(
new ConnectionString(mongodbPrimaryUri)));
}
@Primary
@Bean
public ReactiveMongoTemplate secondaryMongoTemplate() {
return new ReactiveMongoTemplate(
new SimpleReactiveMongoDatabaseFactory(
new ConnectionString(mongodbSecondaryUri)));
}
}
Steps 1 and 2 will replace Spring autoconfigure. Then 3 and 4 will use this config
//# 3. Create Reactive repositories
// PrimaryRepository.java
package com.xyz.primary;
@Repository
public interface PrimaryRepository extends ReactiveMongoRepository<Xyz, String> {}
// SecondaryRepository.java
pacakge com.xyz.secondary;
@Repository
public interface SecondaryRepository extends ReactiveMongoRepository<Xyz, String> {}
//# 4. Create specifig configs
// PrimaryMongoConfig.java
package com.xyz.config;
...
@Configuration
@EnableReactiveMongoRepositories(
basePackages = "com.xyz.primary",
reactiveMongoTemplateRef = "primaryMongoTemplate")
public class PrimaryMongoConfig {}
// SecondaryMongoConfig.java
pacakge com.xyz.config;
...
@Configuration
@EnableReactiveMongoRepositories(
basePackages = "com.xyz.secondary",
reactiveMongoTemplateRef = "secondaryMongoTemplate")
public class SecondaryMongoConfig {}
Notes:
- The respositories have to be in separates sibling pacakges (or at least make sure one is not a subpackage of the other)
- The key part is to specify the
basePackage
where the repository is created - Because they are in different packages the Repositores could have the same name, but will have to be used in code with their fully qualified name if both are used in the same class.
- All of the classes are supplied if the spring-boot-data dependency is already present, if you find yourself needing to import the mongodb driver itself, you're in the wrong track.
- The
SimpleReactiveMongDatabaseFactory
is created using the connection string, which includes the password and database name, you have to be mindful on how to externalize this configuration. There is another constructor, but I didn't go and tried to create a separate MongoClient instance - The existing
spring.data.mondgodb.uri
can be used but I find it better to create separate properties thus:mongodb.primary.uri
andmongodb.secondary.uri
were used.