Spring Data Couchbase community project has been historically built upon the
1.4 generation of the official
Couchbase Java SDK, although the
SDK 2.0 has been out for quite some time.
But now is definitely a great time to upgrade Spring Data Couchbase to the latest 2.2 SDK, especially since it is the one with support for
N1QL, aka. “SQL for Documents”, the new Couchbase query language.
The 2.x SDK generation is a complete rewrite, built upon a fully asynchronous layer and exposing
Observables for the async API. As such it has a separate Maven artifact name, and warrants a major version of Spring Data Couchbase.
The main differences between the 1.x generation of Spring Data Couchbase and its 2.x version are:
- Configuration elements are closer to the Couchbase reality: Environment, Cluster, Bucket (potentially allowing you to create
CouchbaseTemplates that each connect to a different bucket, or even cluster!)
- Backing custom methods is not always done with
viewsanymore, it is now by default rather done via
N1QL, which is much more flexible and requires far less server-side maintenance.
- Custom methods using
viewshave been modified a little to better stick to Spring Data philosophy. This reduces flexibility but the implementations are generated from the method name (“
- View reduction is now supported.
- Changing the name of the JSON field that store type information (“
_class“) is now supported. For example, a
Sync Gateway-compatible one,
javaClass, is offered in
Of course you can still access the lower level API by using the
CouchbaseTemplate rather than a
CouchbaseRepository interface, and you can even access the underlying
Bucket from the SDK.
Let’s dig a little bit deeper into these changes!
Repository methods through N1QL
The big new feature in Couchbase 4.0 is
N1QL, a superset of SQL that works on JSON document (so it added JSON-related specificities to SQL).
This is especially great for the Repository pattern and query derivation in Spring Data, because the vast majority of query derivation keywords are also easily translated to N1QL.
N1QL is now the default backing Couchbase feature for Repository methods. You can also elect to use the
@Query interface if you want to be explicit.
For example, a query-derived method using N1QL could look like:
This in turn, for parameters
"fruit", "cheesecake", 5, gives us a N1QL query looking like:
As you can see, the framework will even correctly choose which fields to select (including metadata) in order to be able to unmarshall the document into a
As for views,
firstX syntax for
countBy instead of
findAllBy to do a
SELECT COUNT(*) are also supported.
Another advantage vs views is that you can have a single general purpose index (a “primary index” from the perspective of N1QL) and use it for all your queries, so this is less server-side tuning compared to views where each different query will need a different view.
Note: of course you can also create more specialized more efficient secondary indexes in N1QL.
Repository methods through Views
One big change in this version is that now, Repository Queries (aka custom repository methods) that are based on views are more in line with the Spring Data philosophy. They also have to be annotated explicitly with
This means that nothing Couchbase-specific should leak into your repository interface. Instead, what you can do is use
query derivation mechanisms for the most simple queries.
This means that you can do something like this:
The framework will take that interface and create a query out of the method name and the given parameters. For example calling it with a
sugar will result in a query like
Please refer to the documentation for an exhaustive list of the keywords in your method name that can get translated to a query parameter. For every other use, you should instead provide the implementation yourself.
Using reduce function from Views
Another new thing that wasn’t previously supported is the execution of the
reduce function if you have one. Now, in order to execute it, you simply declare a method starting with
count instead of
findAll, which returns a long.
Note that the reduce function in Couchbase can be something else than the preexisting
_count one, as long as it returns a long.
Similarly, adding the variation “
topX” or “
firstX” in a method name will result in an additional
limit being set on the request (eg.
findFirst5ByLastName will limit the list to 5 results).
Configuring consistency, Read Your Own Writes
One thing that comes up often when using asynchronously populated secondary indexes like views and
GSI (the new secondary index engine backing N1QL), is the need to
Read Your Own Writes.
This implies that the view/N1QL shouldn’t answer as long as the data is still in the process of being indexed, so this sacrifies some performance in favor of consistency.
The opposite (and current default for Spring Data Couchbase) is to favor performance by accepting stale data to be returned.
We added a global mean of configuring that for all queries (view-based or N1QL-based) that are constructed by the framework through query derivation, by providing a small abstraction around the concept of
This is done by overriding the
Consistency is an enum that lets you choose between
You can also do that in XML by using the
consistency attribute on the
Changing the type information field in stored JSON
Lastly, some users have reported issues with Spring Data and the Couchbase Mobile side of things, with the
Sync Gateway rejecting documents containing fields prefixed by an underscore.
This is problematic for Spring Data stores the type information in a
_class field 🙁
The solution is to allow, through the configuration, to choose the name of that type information field. You can do so by overriding the
typeKey() method in
AbstractCouchbaseConfiguration. For instance, you can use the constant
MappingCouchbaseConverter.TYPEKEY_SYNCGATEWAY_COMPATIBLE (which is “
This field is the one used by generated N1QL queries to filter only documents corresponding to the repository’s entity.
The 2.0 SDK is now closer to the terminology of a Couchbase cluster, with objects like
Bucket as first class citizens. As a result, in the Spring Data Configuration rather than a
CouchbaseClient you configure more diverse beans.
The tuning of the SDK is done in a separate class, the
CouchbaseEnvironment, which allows you to tune io pools, timeouts, etc… Environments should be reused as much as possible accross
Clusters should be reused to open singleton-scoped
Buckets (everything should be reused as much as possible, basically).
To obtain a
CouchbaseTemplate, one needs an
Cluster and a
Bucket. These are all automatically created when extending the JavaConfig
AbstractCouchbaseConfiguration, the only thing you need to provide are:
- the list of nodes to bootstrap from (just the IPs or the hostnames)
- the bucket’s name
- the bucket’s password
Of course, if you want to override, say, the default
Environment configuration, you can override the corresponding methods in the
AbstractCouchbaseConfiguration (as shown in the snippet below):
The equivalent in xml is:
Setting up on the server side
Spring Data Couchbase CRUD methods obtained from a
CrudRepository interface are all still backed by either the Key/Value operations of Couchbase (when working with single entities or mutating entities), or by a
view (when working on multiple entities we don’t know the ID of, for instance for
This means that you still have to have an index of all your entities somehow, in the form of a view in Couchbase.
By default, the framework will look for the name of your entity, uncapitalized, for the design document and a view named
partyCake/all for a
PartyCake entity class).
That’s all (for now) for this first preview version of Spring Data Couchbase 2.0.x. Hope you like it!
Thanks to Oliver from the Spring Data team for his support, and to our users that stepped in and offered contributions or improvement suggestions (not an exhaustive list):
vasilievip, KlausUnger, kdheerendra, jloisel, DWvanGeest, ajbrown, wilsondy
To download and use this preview, use
Maven (with both the
Spring snapshot and
Couchbase snapshot repositories):