Developing on Couchbase CE with Java
Here you can learn how to install and use Couchbase Java SDK
. This tutorial was made using Couchbase Server CE
, so you can use this distribution to follow this guide free of charge.
Java SDK Setup
Tested for
Java 11
andJava SDK 3.0
We used Maven
as the project manager for this tutorial, when installing it, OpenJDK
will get installed too as a dependency.
So open your terminal and type the commands below.
sudo apt update
sudo apt install maven
When that finishes, you have already installed the latest Maven
and Java
versions.
Now you just need to add the following dependency in your project’s pom.xml
file, and you can start coding.
<dependency>
<groupId>com.couchbase.client</groupId>
<artifactId>java-client</artifactId>
<version>3.0.2</version>
</dependency>
For a more extensive install guide, you can follow the Couchbase documentation for Java SDK
Example Project
Most applications we work on, usually require some level of abstraction or framework to interact with data, especially for CRUD
operations. In this example project, we will implement a collection of functions capable of manage ratings, users and movies. Something you would need if working on a movie rating site, as IMDb
for example.
Populate Your Server
Buckets
Couchbase Server
uses Buckets and Collections to store and group documents respectively.
There are three types of buckets according to persistence, and three ways to create them (CLI
, REST API
, and Web UI
). You can have one or more buckets and for each one, you can control access (who can create, delete and list objects in the bucket) and check the logs for access to the bucket and its objects.
Inside each bucket, you can create and use up to 1000 collections
(groups) of documents and up to 100 scopes
to group those collections. Every bucket has a default
collection and scope that will be used if you don’t specifically target a different one.
In this tutorial, we will use a Couchbase Bucket
, which is stored both on memory and disk, and the default collection and scope. Since these last features are still under Developer Preview
you can only use them through the SDK
. Buckets, on the other hand, can be created with the Web UI
on port 8091
.
Go to the Buckets
tab and click on Add Bucket
. Now create a bucket named rate-these-movies
with 100 Mb
using default configurations.

We have already created a sample bucket with the project’s data and saved it to disk using the cbtransfer tool. To import the sample bucket into your server, you must now use the cbrestore tool. These assets are useful to create and restore backups of your buckets or your entire server at any moment.
To import the data open you console and type these comands, make sure to substitute your parameters correctly.
cd /opt/couchbase
cbrestore /path/to/cbb couchbase://hostname:8091 -u username -p password
If everything went well, you should see this message on the console.
[####################] 100.0% (138/estimated 138 msgs)
bucket: b'rate-these-movies', msgs transferred...
: total | last | per sec
byte : 198972 | 198972 | 3161194.6
done
Indexes
Indexes enhance the performance of query and search operations, especially as the buckets
grow in size. Creating secondary indexes (GSI
) on the document values will also let you perform JOIN
operations ON
those.
You can manage Indexes
through the SDK
, this time however we will do it from the Web UI
. Click the Query
tab, and execute the follow line to create the primary index of our bucket.
CREATE PRIMARY INDEX `movies_primary` ON `rate-these-movies`

Then, execute
CREATE INDEX `movies_secondary_movie` ON `rate-these-movies`(`id_movie`)
CREATE INDEX `movies_secondary_user` ON `rate-these-movies`(`id_user`)
to create the secondary indexes needed to perform JOIN
s between movies, users and ratings.
You just executed N1QL
queries on your server, keep reading to learn how to run them using the SDK
as well.
Visualize
The simplest way to verify your data state, and quickly access a particular document, is through Web UI
. Go to Buckets
tab, and click the Documents
button on any bucket.

Click each document for a more extended view, or set some filters to make a specific search.

Using the SDK
Couchbase SDK
provides you with multiple ways to manipulate data:
-
Core operations or key-value operations, are quite basic and will allow you to work with your data similar to how you would do with a dictionary. But, if you want to perform more complex operations like filters or joins, you would have to implement those behaviors on the client-side. Also, they work with the full document, rather than the exact values you may need.
-
Sub-document operations can target specific values in a document. Use these operations to save bandwidth, and be more efficient when consulting partial data.
-
N1QL is an expressive, powerful, and complete
SQL
dialect for querying, transforming, and manipulatingJSON
data. These queries will be interpreted by the server and transformed into core operations. Most queries will require the creation of indexes to join other buckets or decreasing query latency.
Connect
Let’s see now how to establish a connection to the server using the SDK
to open our previously created bucket rate-these-movies
.
This step requires credentials, as a shortcut, you could use the ones used to set up the cluster. Although we don’t recommend this for a production deployment, it fits this tutorial purpose. If you wish to create new credentials with specific permissions, you can follow this link.
To gain access to the server, you can use the Cluster
class. An instance of this class can be used to open buckets and manage data through queries and other operations.
final Cluster cluster = Cluster.connect(hostname, username, password);
final Bucket bucket = cluster.bucket(bucketName);
You don’t need to explicitly disconnect from the server, this will be performed automatically when the instance fall off your code’s scope.
Core Operations
When you need to simply insert, delete or retrieve a particular document, of which you know its ID
, the recommended approach would be to use core operations.
For inserting a document, you can use any of the operations below, the only difference between them is how they react to previously existing documents:
-
insert
will only create the document if the givenID
is not found within the database. -
replace
will only replace the document if the givenID
already exists within the database. -
upsert
will always replace the document, ignoring whether theID
has already existed or not.
Most times, upsert
would be the safest choice, lets use it to add a rating, a standard operation in any rating site.
final JsonObject ratingJSON = JsonObject.create()
.put("movie_id", movieId)
.put("user_id", userId)
.put("value", value);
try {
bucket.defaultCollection().upsert(ratingId, ratingJSON);
System.out.println("OK");
}
catch (Exception e){
System.out.println("ERROR: " + e.getMessage());
}
Notice the use of defaultCollection
, it targets all documents in the bucket. In later versions, you will be able to use collection(name)
to group up documents of a similar type.
Through this tutorial we will use System.out.println
to print answers and give feedback, if you are executing that code inside a function you can return the result or do something else entirely.
Operations like replace
or upsert
can be used to update an existing document. However, remember this will send the full document to the cluster, so as a rule of thumb, do this only when more than half of the values have changed. Later on, we will explain how to update data more efficiently when changes are minimal.
To retrieve documents previously inserted in a bucket, use the get
operation. You can use it now to check the test rating
we just inserted in the server.
try {
final GetResult answer = bucket.defaultCollection().get(ratingId);
System.out.println(answer.contentAs(JsonObject.class));
}
catch (Exception e) {
System.out.println("ERROR: " + e.getMessage());
}
If a user wishes to remove its rating from our server, use the remove
operation. Try it by removing the rating
you have been using until now.
try {
bucket.defaultCollection().remove(ratingId);
System.out.println("OK");
}
catch (Exception e) {
System.out.println("ERROR: " + e.getMessage());
}
Sub-document Operations
Apps will commonly need to change data: ratings for instance, or some miss-typed name. Most of the time this means changing a particular value, not an entire document. For example, a user document may contain a name, a country, and an age, but you only want to update the country the user is currently living. When this situation presents, you should use sub-document operations to target those specific values and reduce network traffic.
Code bellow shows you how to retrieve a particular value from a particular user.
try {
final LookupInResult answer = bucket.defaultCollection().lookupIn(
userId, Collections.singletonList(
LookupInSpec.get("country")));
System.out.println(answer.contentAs(0, String.class));
}
catch (Exception e) {
System.out.println("ERROR: " + e.getMessage());
}
Notice how we target a particular document with lookupIn
, then use get
to retrieve the value we want, in this case, the country.
On the other hand, if a user moves to another country and wishes to update its profile, you can do something like this.
try {
final MutateInResult answer = bucket.defaultCollection().mutateIn(
userId, Collections.singletonList(
MutateInSpec.upsert("country", country)));
System.out.println("OK");
}
catch (Exception e) {
System.out.println("ERROR: " + e.getMessage());
}
Now we use mutateIn
to target the document we want to change, and then upsert
to modify its country value.
N1QL queries
These queries allow us to find and work better with associated documents, as usually required by most applications. For example, if we intend to remove a movie, which has ratings referring to it.
Parameters for the query can be passed in an instance of QueryOptions
, grouped in a JsonArray
. They can get referenced in the query with ?
, in the same order they are at the array.
final QueryOptions parameters = QueryOptions.queryOptions().parameters(JsonArray.from(movieId));
try {
cluster.query("DELETE FROM `rate-these-movies` USE KEYS ?", parameters);
cluster.query("DELETE FROM `rate-these-movies` WHERE id_movie=?", parameters);
System.out.println("OK");
}
catch (Exception e) {
System.out.println("ERROR: " + e.getMessage());
}
Appreciate the simplicity and resemblance to an SQL
query, just refer the bucket as you would with a table.
Another example, most read operations target a subset of data or require some aggregation or augmentation to be performed. So, once again, we will depend on N1QL
queries, in this case, to get the top 5 rated movies along with its average rating.
Since we are joining the data of a bucket with itself we use aliases.
final DecimalFormat df = new DecimalFormat("0.00");
try {
final QueryResult answer = cluster.query("SELECT a.name AS name, AVG(b.`value`) AS avg FROM `rate-these-movies` AS a JOIN `rate-these-movies` AS b ON META(a).id=b.id_movie GROUP BY a.name ORDER BY avg DESC LIMIT 5");
for (JsonObject row : answer.rowsAsObject()) {
System.out.println(row.get("name") + " -> " + df.format(row.get("avg")));
}
}
catch (Exception e) {
System.out.println("ERROR: " + e.getMessage());
}
Next Steps
We recommend you to follow our next tutorials, go to the Getting Started with Couchbase Community Edition page to find the full list.
Also, you could review Couchbase Documentation to learn more about all sorts of topics.