Over a series of 2 posts or so, I would try and show how to develop a simple Todo Desktop application using JavaFX as the Frontend, and the well known NoSQL database MongoDB as the Backend and using Java as the glue between both of them. The main aim of developing this application is to understand how we can use MongoDB and its Java driver to communicate with Java applications.
A brief about MongoDB: It is a document oriented NoSQL database, which stores the data in the form of JSON-like document structure. I can go one and write more, but we can explore as we go along.
In this post, I would cover:
- Installing MongoDB
- Trying out MongoDB using the command line.
- Downloading the Java driver for MongoDB
- Developing the backend for our application.
Installing MongoDB
Download the required MongoDB package from their download site.
Unzip the contents into a directory and lets name it as MONGO_HOME. Update the PATH variable to add the MONGO_HOME/bin. This is useful if you want to run mongo, mongod and other command from any location in your command line.
On your command prompt try $: mongod -version and it should print the version of MongoDB you are using.
Trying out MongoDB using the command line
You have to create a \data\db directory in your C: if you are using Windows or a /data/db directory on Unix, which mongo would use for storing the data.
Start the mongodb database server by running:
C:\Users\sanaulla>mongod mongod --help for help and startup options Wed May 09 22:28:11 [initandlisten] MongoDB starting : pid=4992 port=27017 dbpath=/data/db/ 32-bit [initandlisten] db version v1.8.1, pdfile version 4.5 [initandlisten] waiting for connections on port 27017 [websvr] web admin interface listening on port 28017
Lets connect to this database using the mogndb client.
C:\Users\sanaulla>mongo MongoDB shell version: 1.8.1 connecting to: test
We try out a few simple commands, you can find
> use test switched to db test > show collections > book = {"name":"Book1", "publisher":"publisher1"} { "name" : "Book1", "publisher" : "publisher1" } > db.books.save(book) > db.books.find() { "_id" : ObjectId("4faaa578f65827304120d54b"), "name" : "Book1", "publisher" : "publisher1" }
- use command is used to switch the database whose name is provided after the use. If the db doesn’t exist a new one is created.
- show collections shows the collections created in the db. A collection can be considered equivalent to a table in RDBMS, but with a flexible schema.
- book = {} creates a JSON object which represents a JSON like document and is equivalent to a row in the RDBMS table. And the attributes within the JSON object represent the columns of an RDBMS table.
- db.books.save(book) saves the given JSON object into the collection name which is “books” in this case.
- db.books.find() is used to get all the JSON Objects/rows in the given collections, something similar to SELECT * FROM table_name in SQL.
Downloading the Java driver for MongoDB
Download the Java driver for MongoDB from their Java Language Center page, its a jar which has to be added to your applications classpath.
There are a few basics explained here to get you started with the Java Driver. Dont worry, I will go through few of those as we develop the application further.
Developing the backend for our application
Ah, finally! Lets start with building the backend for our Todo application, lets call it TodoFX and also test the backend via the command line.
Starting with the model class for our application, A Todo would have the task name, status i.e whether it is completed or not, date of adding, and date when it was finished.
//Todo.java import java.util.Date; public class Todo { public Todo(String task, boolean completed, Date added){ this.task = task; this.completed = completed; this.added = added; } public Todo(String task){ this.task = task; this.added = new Date(); this.completed = false; } @Override public String toString(){ return added+": "+this.getTask(); } private String task; private boolean completed; private Date added; private Date finished; public String getTask() { return task; } public void setTask(String task) { this.task = task; } public boolean isCompleted() { return completed; } public void setCompleted(boolean completed) { this.completed = completed; } public Date getAdded() { return added; } public Date getFinished() { return finished; } public void setFinished(Date finished) { this.finished = finished; } }
To start persisting the data, we need a database connection to the MongoDB. The Java Driver provides Mongo class which can be used to obtain the database connection. If we use the default constructor then it returns the database connection to the local database. In this example we would do that. I will try to see if I can connect to a cloud database and show the same.
Mongo mongo = new Mongo();
and then use Mongo#getDB(dbName) to return the database where we would like to store our collections and objects. The database object is of type DB.
DB db = mongo.getDB(name);
I extracted these into a different class called DbManager
//DbManager.java import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.Mongo; import java.net.UnknownHostException; public class DbManager { private static DB db; public static DB getDb (String name) throws UnknownHostException { Mongo mongo = new Mongo(); if ( db == null){ db = mongo.getDB(name); } return db; } }
Ok, so its time to see how we can SAVE and FIND all the todos added. Before that, our database name is: “todoapp” and the collection name is: “todo”
Saving the Todo instance to the database:
Create a DBObject from the data present in the Todo instance. I prefer to use the BasicDBObjectBuilder to create an instance of DBObject.
DBObject dbObject = BasicDBObjectBuilder.start() .add("task",todo.getTask()) .add("completed",todo.isCompleted()) .add("added",todo.getAdded()) .get();
the BasicDBObjectBuilder#add(), takes in a key, value pair where the key represents the column name and the value represents the item in the column. The advantage with NoSQL is that there is no requirement of a rigid schema. I could have one row with 3 keys, another row with 4 keys and so on.
After creating the DBObject, it has to be added to the collection and then persisted to the database.
DB db = DbManager.getDb("todoapp"); DBCollection dbCollection = db.getCollection("todo"); dbCollection.save(dbObject);
Pretty straight forward right? And here you are done with persisting your data to the database. How simple can this get? I just removed the fear of PreparedStatments, Connections, SQLExceptions and what not.
Note: In all the cases the DbManager is the class which we have created somewhere near the starting of this example.
Retrieving all the Todos from the database:
- Get the database and fetch the collection from it.
- Iterate through the collection and get each of the values for the available keys(or columns)
- Create instance of Todo from these retrieved values.
Let me show the code:
DB db = DbManager.getDb(DBNAME); DBCollection dbCollection = db.getCollection(COLLECTION_NAME); DBCursor dbCursor = dbCollection.find(); List allTodos = new ArrayList(); while ( dbCursor.hasNext()){ DBObject dbObject = dbCursor.next(); String task = String.valueOf(dbObject.get("task")); Date added = (Date)dbObject.get("added"); boolean completed = (Boolean)dbObject.get("completed"); Todo todo = new Todo(task,completed, added); allTodos.add(todo); } return allTodos;
One new thing is the use of DBCursor, which is an iterator over the database results (think ResultSet).
Let me call this class which provides save() and find() as TodoDAO
//TodoDAO.java import com.mongodb.*; import java.net.UnknownHostException; import java.util.*; public class TodoDAO { private static final String DBNAME = "todoapp"; private static final String COLLECTION_NAME = "todo"; public static void saveTodo(Todo todo) throws UnknownHostException{ DBObject dbObject = BasicDBObjectBuilder.start() .add("task",todo.getTask()) .add("completed",todo.isCompleted()) .add("added",todo.getAdded()) .get(); DB db = DbManager.getDb(DBNAME); DBCollection dbCollection = db.getCollection(COLLECTION_NAME); dbCollection.save(dbObject); } public static List getAllTodos() throws UnknownHostException{ DB db = DbManager.getDb(DBNAME); DBCollection dbCollection = db.getCollection(COLLECTION_NAME); DBCursor dbCursor = dbCollection.find(); List allTodos = new ArrayList(); while ( dbCursor.hasNext()){ DBObject dbObject = dbCursor.next(); String task = String.valueOf(dbObject.get("task")); Date added = (Date)dbObject.get("added"); boolean completed = (Boolean)dbObject.get("completed"); Todo todo = new Todo(task,completed, added); allTodos.add(todo); } return allTodos; } }
I think with this we have pretty much the required backend to build further. There are lot of aspects of query, like providing the WHERE clauses, then we can do indexing. I havent explored all those, and I would surely write about it as and when I explore.
Let me try and run this via command line:
//TodoAppRunner.java public class TodoAppRunner{ public static void main(String[] args) throws UnknownHostException{ Todo todo = new Todo("Task 1 from Command Line"); TodoDAO.saveTodo(todo); todo = new Todo("Task 2 from Command Line"); TodoDAO.saveTodo(todo); todo = new Todo("Task 3 from Command Line"); TodoDAO.saveTodo(todo); List allTodos = TodoDAO.getAllTodos(); for ( Todo aTodo : allTodos){ System.out.println(aTodo); } } }
Output:
Wed May 09 23:42:07 IST 2012: Task 1 from Command Line Wed May 09 23:42:09 IST 2012: Task 2 from Command Line Wed May 09 23:42:09 IST 2012: Task 3 from Command Line
I have used IntelliJ Idea IDE for this sample application.
Next: Building the Add New Todo UI using JavaFX.