Connecting to MongoDB with Spring and the Reactive Streams Driver

One of the first tasks when using MongoDB and Spring is to create a com.mongodb.reactivestreams.client.MongoClient object by using the IoC container.

Registering a MongoClient Instance Using Java-based Metadata

The following example shows how to use Java-based bean metadata to register an instance of a com.mongodb.reactivestreams.client.MongoClient:

Example 1. Registering a com.mongodb.MongoClient object using Java based bean metadata
@Configuration
public class AppConfig {

  /*
   * Use the Reactive Streams Mongo Client API to create a com.mongodb.reactivestreams.client.MongoClient instance.
   */
   public @Bean MongoClient reactiveMongoClient()  {
       return MongoClients.create("mongodb://localhost");
   }
}

This approach lets you use the standard com.mongodb.reactivestreams.client.MongoClient API (which you may already know).

An alternative is to register an instance of com.mongodb.reactivestreams.client.MongoClient instance with the container by using Spring’s ReactiveMongoClientFactoryBean. As compared to instantiating a com.mongodb.reactivestreams.client.MongoClient instance directly, the FactoryBean approach has the added advantage of also providing the container with an ExceptionTranslator implementation that translates MongoDB exceptions to exceptions in Spring’s portable DataAccessException hierarchy for data access classes annotated with the @Repository annotation. This hierarchy and use of @Repository is described in Spring’s DAO support features.

The following example shows Java-based bean metadata that supports exception translation on @Repository annotated classes:

Example 2. Registering a com.mongodb.MongoClient object using Spring’s MongoClientFactoryBean and enabling Spring’s exception translation support
@Configuration
public class AppConfig {

    /*
     * Factory bean that creates the com.mongodb.reactivestreams.client.MongoClient instance
     */
     public @Bean ReactiveMongoClientFactoryBean mongoClient() {

          ReactiveMongoClientFactoryBean clientFactory = new ReactiveMongoClientFactoryBean();
          clientFactory.setHost("localhost");

          return clientFactory;
     }
}

To access the com.mongodb.reactivestreams.client.MongoClient object created by the ReactiveMongoClientFactoryBean in other @Configuration or your own classes, get the MongoClient from the context.

The ReactiveMongoDatabaseFactory Interface

While com.mongodb.reactivestreams.client.MongoClient is the entry point to the reactive MongoDB driver API, connecting to a specific MongoDB database instance requires additional information, such as the database name. With that information, you can obtain a com.mongodb.reactivestreams.client.MongoDatabase object and access all the functionality of a specific MongoDB database instance. Spring provides the org.springframework.data.mongodb.core.ReactiveMongoDatabaseFactory interface to bootstrap connectivity to the database. The following listing shows the ReactiveMongoDatabaseFactory interface:

public interface ReactiveMongoDatabaseFactory {

  /**
   * Creates a default {@link MongoDatabase} instance.
   *
   * @return
   * @throws DataAccessException
   */
  MongoDatabase getMongoDatabase() throws DataAccessException;

  /**
   * Creates a {@link MongoDatabase} instance to access the database with the given name.
   *
   * @param dbName must not be {@literal null} or empty.
   * @return
   * @throws DataAccessException
   */
  MongoDatabase getMongoDatabase(String dbName) throws DataAccessException;

  /**
   * Exposes a shared {@link MongoExceptionTranslator}.
   *
   * @return will never be {@literal null}.
   */
  PersistenceExceptionTranslator getExceptionTranslator();
}

The org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory class implements the ReactiveMongoDatabaseFactory interface and is created with a standard com.mongodb.reactivestreams.client.MongoClient instance and the database name.

Instead of using the IoC container to create an instance of ReactiveMongoTemplate, you can use them in standard Java code, as follows:

public class MongoApp {

  private static final Log log = LogFactory.getLog(MongoApp.class);

  public static void main(String[] args) throws Exception {

    ReactiveMongoOperations mongoOps = new ReactiveMongoOperations(new SimpleReactiveMongoDatabaseFactory(MongoClient.create(), "database"));

    mongoOps.insert(new Person("Joe", 34))
        .flatMap(p -> mongoOps.findOne(new Query(where("name").is("Joe")), Person.class))
        .doOnNext(person -> log.info(person.toString()))
        .flatMap(person -> mongoOps.dropCollection("person"))
        .subscribe();
  }
}

The use of SimpleMongoDbFactory is the only difference between the listing shown in the getting started section.

Registering a ReactiveMongoDatabaseFactory Instance by Using Java-based Metadata

To register a ReactiveMongoDatabaseFactory instance with the container, you can write code much like what was highlighted in the previous code listing, as the following example shows:

@Configuration
public class MongoConfiguration {

  public @Bean ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory() {
    return new SimpleReactiveMongoDatabaseFactory(MongoClients.create(), "database");
  }
}

To define the username and password, create a MongoDB connection string and pass it into the factory method, as the next listing shows. The following listing also shows how to use ReactiveMongoDatabaseFactory to register an instance of ReactiveMongoTemplate with the container:

@Configuration
public class MongoConfiguration {

  public @Bean ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory() {
    return new SimpleReactiveMongoDatabaseFactory(MongoClients.create("mongodb://joe:secret@localhost"), "database");
  }

  public @Bean ReactiveMongoTemplate reactiveMongoTemplate() {
    return new ReactiveMongoTemplate(reactiveMongoDatabaseFactory());
  }
}