NoSQL with Couchbase: Working with JSON

Keep calm and JSON

Welcome to the third episode of the Couchbase series. In Couchbase everything (except for counters) are stored in JSON and in this article, we will look into how we can work with it in an easy and generic way.

Make sure to check out the other articles in the series as well:

  1. NoSQL with Couchbase: Setting everything up with Docker
  2. NoSQL with Couchbase: Getting started
  3. NoSQL with Couchbase: Working with JSON
  4. NoSQL with Couchbase: Querying with N1QL
  5. NoSQL with Couchbase: Creating Indexes that Scale

There are two terms that you should be aware of and understand what they mean in order to not get confused in this article. The first one is serialization which is the process of converting an object to a stream of bytes so that it can be sent over the network or persisted to disk. Deserialization is the opposite, meaning how we can transform that stream of bytes into an object. There are different ways to interpret or manipulate these stream of bytes, popular options are XML or JSON. We will work with JSON in these articles.

Serializing and deserializing JSON

In the last article, we coded a repository for persisting and reading person objects to/from Couchbase. We used an ObjectMapper from Jackson to do the serialization and deserialization. But we didn’t do it in a very clean and re-usable way which is the first thing I want to go back and refactor.

We are going to make a serializer class that will handle it for us in a nice and generic way.

This class is able to both serialize and deserialize any different types of objects. This means that we can put all the error handling and avoid having try-catch in our repository. It also means that we easy can re-use this serializer for any other repository that we later create.

Our updated PersonRepository class utilizing this new serializer looks like the following:

Much more neat and cleaner than the previous version.

Improvements in our Person class

The next thing we need to do is to make our Person object more accessible for working with JSON when it is persisted in Couchbase. We also are going to use make use of CAS (Check and Set) which is a very powerful way to handle concurrent modifications of our documents.

First of all, we are going to add a type to our Person class. This type has a very important purpose later for our N1QL queries. N1QL is like SQL, but for JSON, which is one of Couchbase specialties. You get the incredible speed of key/value fetches, but at the same time, it also supports more advanced fetches via N1QL. The type field is important when we are going to create our indexes that will support N1QL queries, but more on that in a future blog but while we are doing work in our Person class we might as well add this type value already.

For every Couchbase document we want to store, we want it to have 3 different values: type, cas and id, it doesn’t matter if its a person or a car or any other arbitrary object. That sounds like perfect for an interface.

We use @JsonIgnore to instruct the ObjectMapper to not serialize and deserialize id and cas. The reason for this is that these are already stored in the metadata inside Couchbase, and we are not interested in storing them again inside the actual document. But at the same time, we do need these values in our application while working with the objects that the documents represent.

Our updated Person class which implements this interface looks like the following.

We put the handling of cas and id inside our serializer class so that we never have to remember to bother with it and risk forgetting it.

We also make some minor adjustments in our PersonRepository class that uses the serializer.

Notice how we pass the cas value in the RawJsonDocument.create(String, String, long) method. This is a security mechanic against concurrent modifications. If the cas value has been updated by someone else before we did our upsert we will receive a ConcurrentModificationException, and we will know that someone else concurrently modified our document. At this point there are many different approaches to take, we can abort the procedure and let the user know that someone already updated the document, or we can attempt to receive the document and try to merge in our changes as well. This really depends on the type of application, and there is no generic right answer on how to handle ConcurrentModificationExceptions. But at least if we pass the cas value, we can be sure that we never accidentally override some other update to the document, if we do not care about this we can simply avoid passing the cas value.

Final words

With this, we have implemented a nice and reliable way to handle serializing and deserializing JSON. We did it in a generic way that very easily can be re-used by any other type of objects that we want to store in Couchbase, all they have to do is to implement the CouchbaseDocument interface.

In the next article, we will evolve our Person class and add some more fields to it so that we can start running some N1QL queries against it.

You may also like

Leave a Reply

Your email address will not be published. Required fields are marked *