We all know how awesome NoSQL databases, such as mongodb, are. They come with amazing features & blazing fast access speed. However, they still lack a feature or two when compared to the traditional relational databases.
One such feature is the ability to have a sequence generation capability. One must have come across
@GeneratedValue(strategy = GenerationType.AUTO) in spring. It enables programmers to use the underlying database’s sequence generation features to have an attribute, most commonly the id/primary key, numerically increment itself. This is an fantastic feature that comes built-in in most traditional relational databases such as MySQL, MSSQL, etc.
Unfortunately though, this is something still missing in mongodb. I recently came across a use case wherein I had to work with mongodb and create a sequence Id attribute in one of my collections. In this post I will elucidate the same with examples.
To persist a collection to have an attribute that automatically increments itself upon the insertion of a new record.
Since this is not something that comes out of mongodb, we will need a bit of application code in order to accomplish this task, and we will be using mongodb along with spring to get it done.
In the above snippet, the attribute, namely
seqId, we will autoincremented.
Firstly, we need a collection in which we can save the sequence for a collection. Yes, this is a collection which we will increment upon each insertion operation on our target document.
Secondly, we need to create a function that will query this
MongoSequence collection before the
Job document is inserted.
Okay, so there are a couple of things going on in the above code. Naturally, we are first querying the
MongoSequence collection and incrementing the value for
sequence for our
However, we need to be careful about using something like this in a multithreaded environment such as web applications where database operations such as this may get executed in multiple threads simultaneously. Thus, we need a mechanism to execute this function once at a time. To accomplish this, we are using spring’s
@Transactional annotation. While using this, there are several options we can select for ‘
We can select from the followings.
@Transactional(isolation = Isolation.DEFAULT)
@Transactional(isolation = Isolation.READ_COMMITTED)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Transactional(isolation = Isolation.REPEATABLE_READ)
@Transactional(isolation = Isolation.SERIALIZABLE)
When we do not pass any isolation level value, spring automatically uses
Isolation.DEFAULT , and this level of isolation means that spring will delegate transaction management to the management logic provided by the backing database. However, that may not always be threadsafe. Thus, in my code, I am using
@Transactional(isolation = Isolation.SERIALIZABLE) , which is the most strict level of isolation.
This isolation basically means that the function marked with this annotation will be run at a time by each thread while other threads will be waiting for their turn. This will ensure that each insert gets a unique, auto incremented value for seqId.
Lastly, we need to initialize our counter collection with a value for the collection for which we want to generate sequences. This is, in simple words, a insert operation in our
MongoSequence collection with the value of
sequence set to 0.
One must keep in mind that although this solves our problems, it does come with a bit of a performance penalty since it stops multiple threads from simultaneously performing insert operations. That is one trade-off we have to make until the folks behind mongodb make this feature something that is available out-of-the-box.
That is the end of it. Cheers!