Spring-data-mongodb connect to multiple databases in one Mongo instance Spring-data-mongodb connect to multiple databases in one Mongo instance spring spring

Spring-data-mongodb connect to multiple databases in one Mongo instance


Here is a link to an article I think is what you are looking for http://michaelbarnesjr.wordpress.com/2012/01/19/spring-data-mongo/

The key is to provide multiple templates

configure a template for each database.

<bean id="vehicleTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">    <constructor-arg ref="mongoConnection"/>    <constructor-arg name="databaseName" value="vehicledatabase"/></bean>

configure a template for each database.

<bean id="imageTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">        <constructor-arg ref="mongoConnection"/>        <constructor-arg name="databaseName" value="imagedatabase"/></bean><bean id="vehicleTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">    <constructor-arg ref="mongoConnection"/>    <constructor-arg name="databaseName" value="vehicledatabase"/></bean>

Now, you need to tell Spring where your repositories are so it can inject them. They must all be in the same directory. I tried to have them in different sub-directories, and it did not work correctly. So they are all in the repository directory.

<mongo:repositories base-package="my.package.repository">    <mongo:repository id="imageRepository" mongo-template-ref="imageTemplate"/>    <mongo:repository id="carRepository" mongo-template-ref="vehicleTemplate"/>    <mongo:repository id="truckRepository" mongo-template-ref="vehicleTemplate"/></mongo:repositories>

Each repository is an Interface and is written as follows (yes, you can leave them blank):

@Repositorypublic interface ImageRepository extends MongoRepository<Image, String> {}@Repositorypublic interface TruckRepository extends MongoRepository<Truck, String> {}

The name of the private variable imageRepository is the collection! Image.java will be saved to the image collection within the imagedb database.

Here is how you can find, insert, and delete records:

@Servicepublic class ImageService {    @Autowired    private ImageRepository imageRepository;}

By Autowiring you match the variable name to the name (id) in your configuration.


So after much research and experimentation, I have concluded that this is not yet possibly with the current spring-data-mongodb project. I tried baja's method above and ran into a specific hurdle. The MongoTemplate runs its ensureIndexes() method from within its constructor. This method calls out the the database to make sure annotated indexes exist in the database. The constructor for MongoTemplate gets called when Spring starts up so I never even have a chance to set a ThreadLocal variable. I have to have a default already set when Spring starts, then change it when a request comes in. This is not allowable because I don't want nor do I have a default database.

All was not lost though. Our original plan was to have each client running on its own application server, pointed at its own MongoDB database on the MongoDB server. Then we can provide a -Dprovider= system variable and each server runs pointing only to one database.

We were instructed to have a multi-tenant application, hence the attempt at the ThreadLocal variable. But since it did not work, we were able to run the application the way we had originally designed.

I believe there is a way though to make this all work, it just takes more than is described in the other posts. You have to make your own RepositoryFactoryBean. Here is the example from the Spring Data MongoDB Reference Docs. You would still have to implement your own MongoTemplate and delay or remove the ensureIndexes() call. But you would have to rewrite a few classes to make sure your MongoTemplate is called instead of Spring's. In other words, a lot of work. Work that I would like to see happen or even do, I just did not have the time.

Thanks for the responses.


You may want to sub-class SimpleMongoDbFactory and strategize how the default DB as returned by getDb is returned. One option is to use thread-local variables to decide on the Db to use, instead of using multiple MongoTemplates.

Something like this:

public class ThreadLocalDbNameMongoDbFactory extends SimpleMongoDbFactory {    private static final ThreadLocal<String> dbName = new ThreadLocal<String>();    private final String defaultName; // init in c'tor before calling super    // omitted constructor for clarity    public static void setDefaultNameForCurrentThread(String tlName) {        dbName.set(tlName);    }    public static void clearDefaultNameForCurrentThread() {        dbName.remove();    }    public DB getDb() {        String tlName = dbName.get();        return super.getDb(tlName != null ? tlName : defaultName);    }}

Then, override mongoDBFactory() in your @Configuration class that extends from AbstractMongoConfiguration like so:

@Bean@Overridepublic MongoDbFactory mongoDbFactory() throws Exception {  if (getUserCredentials() == null) {      return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName());  } else {      return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials());  }}

In your client code (maybe a ServletFilter or some such) you will need to call:ThreadLocalDBNameMongoRepository.setDefaultNameForCurrentThread()before doing any Mongo work and subsequently reset it with:ThreadLocalDBNameMongoRepository.clearDefaultNameForCurrentThread()after you are done.