Engineering
Releases
News and Events

New in Spring Data Lovelace M2 - Get ready for MongoDB 3.6 and 4.0.

With the latest Spring Data Lovelace Milestone 2 release, the MongoDB module is stacking up new features that are coming your way in the near future. As you might have followed in the news, MongoDB 4 is going to bring ACID transactions to the Document store. The latest MongoDB 3.6 server release already ships with the main building block for those, client sessions.

You can read all about isolation levels and causal consistency in the MongoDB reference. In short, sessions let you execute operations in an order that respects their causal relationships.

With Spring Data MongoDB, ClientSession is right at your fingertips for both the imperative and the reactive world, as we have incorporated those into the already existing MongoOperations and ReactiveMongoOperations. To provide you with the utmost control and still enough convenience, managing the ClientSession lifecycle is up you, while the template takes care of passing the session on to the driver correctly. The following example shows how to create a ClientSession:

ClientSessionOptions sessionOptions = ClientSessionOptions.builder()
    .causallyConsistent(true)
    .build();

ClientSession session = client.startSession(sessionOptions);

Once a session is fetched from the MongoDB Java Driver, all you need to do is add it to the template to retrieve session-bound operations and close() the session when done, as shown in the following example:

MongoOperations sessionBound = template.withSession(session);

Query query = query(where("name").is("Durzo Blint")
Person durzo = sessionBound.findOne(query), Person.class);

Person kylar = new Person("Kylar Stern");
kylar.setMaster(durzo);

sessionBound.insert(kylar);

session.close();

Using a reactive programming model, the above example looks a bit different, since we’re about to obtain a session Publisher and want to make sure to not resolve it before actually subscribing.
So, instead of obtaining a session-scoped ReactiveMongoTemplate, we get an instance of ReactiveSessionScoped that holds a reference to the session Publisher and offers execution and close handles, so that we can make sure to close the ClientSession in the finally block of the returned Flux, as shown in the following example:

ClientSessionOptions sessionOptions = ClientSessionOptions.builder()
    .causallyConsistent(true)
    .build();

// obtain a session, but do not subscribe to it
Publisher<ClientSession> session = client.startSession(sessionOptions);

template.withSession(session)
    .execute(action -> {

        Query query = query(where("name").is("Durzo Blint"));
        return action.findOne(query, Person.class)
            .flatMap(durzo -> {

                Person kylar = new Person("Kylar Stern");
                kylar.setMaster(durzo);

                return action.insert(kylar);
            });
    }, ClientSession::close) // make sure we close the session when finished
    .subscribe();

So far, so good, but didn’t we mention transactions at the top of this article?

True, transactions will be part of MongoDB 4.0 and, yes, we’re going to be ready for it. If you want to follow our efforts towards transaction support, make sure to follow the development. Here’s a quick glance at what you can expect.

MongoTransactionManager will be the gateway to the well known transaction support, allowing applications to use the managed transaction features of Spring.
The MongoTransactionManager will create and bind a ClientSession to the thread. Transactions are started, committed, or aborted, while MongoTemplate automatically detects existing ClientSessions and operates on them accordingly, as shown in the following example:

@Configuration
public class Config extends AbstractMongoConfiguration {

  @Bean
  MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
    return new MongoTransactionManager(dbFactory);
  }

  // ...
}

@Service
public class NightAngel {

  @Autowired PersonRepository repository;

  @Transactional
  public void trainAzoth() {

    Person durzo = repository.findByName("Durzo Blint");

    Person kylar = new Person("Kylar Stern");
    kylar.setMaster(durzo);

    repository.save(kylar);    
  }
}
comments powered by Disqus