The JVM drivers team is pleased to announce the release of the 3.0 Java Driver. This release is highlighted by a number of new features: an extensible Codec API that allows applications to encode and decode their own classes directly to and from BSON; a new CRUD API, based on the cross-driver CRUD specification; and finally, a callback-based asynchronous version of the CRUD API.

The road to 3.0

Since the 2.0 version of the Java driver was released nearly five years ago, there have been 13 minor releases, with very few binary compatibility breaks. While this stability has been important in allowing users to freely upgrade to the latest release, significant technical debt has accumulated in both its internal design and its public API.

We addressed some of the internal design debt early in the 3.0 development cycle by re-writing three key components of the driver: the connection pool, the server monitor, and the server selector. These were completed a year ago, and, in order to get them into production as fast as possible, they were backported and included in the 2.12 release that shipped along side MongoDB 2.6. This provided the initial field testing of some of the ideas in what later became the cross-driver Server Discovery and Monitoring and Server Selection specifications.

That left the technical debt in our public API and we could only address that by breaking backwards compatibility. In accordance with semantic versioning that required a major version bump -- and thus 3.0 was born.

We worked hard to limit the number of breaking changes so that upgrading applications to 3.0 would be relatively smooth. Here are the questions we asked ourselves about each breaking change that we were considering:

Is this class or method likely to be used by applications? If the answer was no, we were more likely to consider removing it, especially if it no longer served any purpose or had been replaced by something better. There were dozens of candidates that, when examined, turned out to be dispensable. Would the continued existence of this class or method force us to sacrifice important design principles of the driver? Again, if the answer was yes, we were more likely to consider removing it. Happily, there weren’t many of these, but a good example is the WriteResult.getLastError method. This method allows an application to execute an unacknowledged write, and at an unbounded future time request acknowledgement. This method is nearly impossible to support correctly, and since there is an alternative (acknowledge the write immediately), the method has been removed. Does the class or method have ill-defined or inconsistent semantics? Again, if yes, we considered removal. There weren’t many of these either, but a prominent one in this category is DB.requestStart , and so it’s been removed as well.

For a more complete run-down of the most significant breaking changes in 3.0, please refer to the Upgrade Guide.

New Database and Collection Classes

One of our main goals in releasing the 3.0 driver was to address some fundamental issues with the existing DB and DBCollection classes:

Parameters that represent BSON documents are all of type DBObject, so applications which use domain-specific classes must first translate instances to DBObject instances, which reduces efficiency.

Their properties are mutable and there is only one instance created per database or collection name, so different parts of an application can easily step on each other by, say, changing the default read preference or write concern.

The classes are concrete so are difficult to wrap. Many have asked that they be changed to interfaces.

There is a surfeit of overloaded methods for various combinations of optional arguments.

At the same time, we didn’t want to abandon the DB/DBCollection API, as that would make upgrading existing applications to 3.0 a nightmare, discouraging its adoption by existing users. So we decided to leave these classes alone in 3.0, and introduced a new set of classes that addressed all these problems: MongoDatabase and MongoCollection .

So for users of the 2.x driver series that are upgrading to 3.0, the driver still has the MongoClient.getDB("myDB") method and, although it has been deprecated, rest easy -- it won't be removed in the 3.x series. The deprecation is intended to nudge new applications towards the MongoDatabase/MongoCollection API, which is accessible via the new MongoClient.getDatabase("myDB") method. This way we retain backwards compatibility while still offering an improved API that users can migrate to at their own pace.

The MongoCollection interface contains just 57 methods, down from 107 in the DBCollection class, and at the same time gains functionality and improved consistency across methods. To help future proof the API we've used Options classes for methods arguments, which allows us to extend the API without cluttering it with more method overloads as functionality is added to future versions of MongoDB.

MongoCollection and MongoDatabase are defined as interfaces, and instances are immutable. Overloads are limited to situations where the extra parameter changes the return type of the method.

A popular feature request has been to provide an easier way to convert the results from the database straight into domain objects. With the introduction of a Codec registry and the use of generics in the API, working directly with domain objects is now possible. So if the new Document class isn't to your taste, then register a custom codec for your domain object and simply use the API and pass in the class of the domain object you want to use. For example:

MongoCursor<Document> cursor1 = coll.find().iterator(); MongoCursor<Person> cursor2 = coll.find(Person.class).iterator();

If you always want to use a default document class with your collection you can easily change it:

MongoCollection<Person> personColl = coll.withDocumentClass(Person.class); MongoCursor<Person> cursor = personColl.find().iterator();

Methods that return streams of values, like find, aggregate, mapReduce, and distinct, all present fluent APIs that are defined as implementations of Java’s Iterable interface.

collection.find().sort(new Document("offerAmount", 1)).skip(10).limit(30);

There are two core benefits to the above implementation: the API is consistent and also future-compatible, as additions to the fluent interfaces can be made without having to increase the number of methods to the underlying collection API.

Async Driver

You asked, we answered. There's now a brand new, asynchronous driver as part of 3.0. When abstracting the core library we ensured that it supported both synchronous and asynchronous execution, on top of which we’ve written a new asynchronous library. The async driver follows the idioms set by the new API in the Java driver but uses callbacks to provide non-blocking behavior.

In the following example we get all the names out of the collection and in the callback we print those names:

coll.find().map(new Function<Document, String>() { @Override public String apply(final Document document) { return document.getString("name"); } }).into(names, new SingleResultCallback<List<String>>() { @Override public void onResult(final List<String> names, final Throwable t) { if (t != null) { System.out.println("There was an error: " + t); } else { System.out.println("Names: " + names); } } });

Getting Started

To get started with using the new Java driver, have a look at the getting started with the Java Driver quick tour. We look forward to getting your feedback!