Implementing Entity Services using NoSQL – Part 3: CouchDB
As I already mentioned in Part 1, Iâve already selected CouchDB as my NoSQL database and the Ektorp library as the database driver.
CouchDB â The Relaxed Database.
CouchDB is essentially going to act as my document store. Natively it stores its documents in JSON format (actually a binary version called BSON I think) and it requires no prior knowledge of the documentâs structure. You can pretty much store anything, and even mix documents of different types in the same database if you want to.
Because there are no prior setup steps such as DDL scripts or XSD schemas, getting started can be very quick. In fact if you can use CURL you donât need anything, just issue HTTP commands to CouchDB and your stuff gets stored. As it says on the tin â CouchDB is fairly relaxed. For a fuller explanation on getting started with the basics of CouchDB see CouchDB: The Definitive Guide.
Getting from Java to JSON format.
Of course when weâre in Java, JSON as a String based representation isnât that convenient for us to use. This is where Ektorp steps in, using the Jackson Java-to-JSON library to smooth things over. Jackson facilitates an out of the box POJO to JSON conversion process behind the scenes within Ektorp.
Jackson is an important feature for this project because I want to acheive a clean and hassle free development flow from XML to Java Objects to Database Documents and back again. Jackson is a key component in making this work as weâll see later.
CouchDBâs Document Storage Pre-Requisites.
Although CouchDB doesnât need a schema, it does need two basic pieces of data for each document: a unique id and a document revision number. These data items help with managing the documents and implementing the idempotency rules that help maintain document integrity in multi user environments. CouchDB expects these fields to be named â_idâ and â_revisionâ. _id can be assigned by the user or by the database during create operations. _revision is assigned by the database and increments upwards each time a documentâs record is updated.
Now obviously I didnât want database specific fields to go into my XML documents, so my definition of a product has a field called âIdâ and a field called âRevisionâ. Unless I do something, this document would not meet the necessary criteria for storage in CouchDB, and strange things would start to happen like extra _id and _revision fields being added to the database records at runtime that didnât match the Id and Revision of the XML document I asked CouchDB to store. I donât want to change my XML schema for a Product in order to add these database specific fields, so what do I do?
Cleverly, Jackson can be configured to rectify this problem without touching the Java/JaxB definition of the âProductâ object that is derived from the Product XML schema. It can be told to remap the Productâs âIdâ and âRevisionâ fields to the CouchDb â_idâ and â_revisionâ fields at runtime. This maintains a degree of loose coupling but allows me to use the same JaxB generated Java objects throughout my code saving a lot of time and effort.
Accessing the Database.
CouchDB is not accessed via JDBC and it doesnât have a traditional JDBC driver. Instead it uses a REST interface (http based GET, PUT, POST, DELETE, etc.) and communicates using JSON formatted content.
Ektorp provides some helper classes to help you work with the CouchDB database. There is a Connector class that can be instantiated to establish a workable connection to the database, and a customisable RepositorySupport class that offers type-safe convenience methods for interacting with the database and its records.
Creating a DAO.
Once correctly customised by extension and class-typing, the RepositorySupport class can be used for all your basic Data Access Object requirements such as Get, Create, Update and Remove operations. It can also generate CouchDB views automatically based purely on the name of the methods you add to it (as long as they follow certain rules). This makes it easy to add âfindâ methods to your DAO such as âfindByManufacturerNameâ or âfindByCategoryIdâ. Finally, if you need more sophisticated views or map/reduce queries, it can help with those too.
Pulling it all together.
By configuring Jackson and by using Ektorp to create a DAO, itâs now just a case of writing some integration tests to make sure it all hangs together. The tests I used initially are quite simple, I asked my DAO toâĶ
- create a fresh JaxB Product object and assign it an ID
- save it to my CouchDB âProductâ database
- read the Product object from the âProductâ database using itâs ID
- modify the Product object & update it
- retrieve it once more, checking the revision was increased
- finally, delete the Product object & check that attempts to read it now fail
If the DAO code can do all these things, then I have the basic behaviours that I need for my Product Entity Service implementation. However, because itâs an integration test, I need a working CouchDB service to be available during the testing cycle. Maven can help with integration testing by using the Maven Failsafe plugin to bind these kinds of tests to the integration-testing specific parts of the Maven build lifecycle. This prevents mixing integration tests in with normal unit tests which usually have fewer dependencies and runtime requirements.
Getting CouchDB working locally is pretty simple, but itâs also possible to use a free cloud hosted CouchDB development instance if you canât be bothered with the install and set-up process. Iâve tried both, and they work equally well.
Whatâs next?
Now my CouchDB DAO is complete itâs time to move into the final stages of the project where Iâll link up the DAO behaviours to the Web Service capabilities I created earlier. To do this Iâll be using Java Enterprise Edition 6.
If youâd like an email notification when the next instalment is published then follow the link on the right to subscribe.
Continue to Part 4.
Reference: Implementing Entity Services using NoSQL â Part 3: CouchDB from our JCG partner Ben Wilcock at the SOA, BPM, Agile & Java blog.




