This tutorial explains about the Jongo, a Java query language for accessing the data from MongoDB database. When you work with the MongoDB database, you have to use the Mongo shell for querying the data, however when you are querying in the Java application, you have to use the Java driver for MongoDB and retrieve the data. This approach is not easy and needs complex syntax. Jongo addresses the problem by Query in Java as in Mongo shell.
MongoDB is an open-source noSQL database written in C++ that provides high performance, high availability and auto-scaling. A record in a MongoDB is called a Document. A document is a data structure composed of keys and corresponding values. MongoDB documents are similar to JSON objects as shown:
Java MongoDB Driver
MongoDB provides a Java driver to communicate with MongoDB database. Please follow ttp://docs.mongodb.org/ecosystem/drivers/java for more details on how to connect to a MongoDB server via Java application.
DBObject API
The Java driver for MongoDB uses DBObject
interface and its subclasses for persisting and representing documents in the database. This API provides bare bones methods to persist and retrieve Java objects into MongoDB database.
Below is the class diagram of DBObject API
Lets use the DBObject to persist a Java POJO into MongoDB.
Person.java
public class Person { private String _id; private String firstName; private String lastName; private Address address; //getters & setters }
Address.java
public class Address { private String houseNo; private String street; private String city; private String country; private String zip; }
There are two POJOs Address and Person and Address is referenced inside Person. We’ll first try to insert an instance of the Person as Mongodb Document. Also, let’s create a Mongo Persistence DAO and its corresponding implementation.
MongoService.java
public interface MongoService<T> { Object save (T t); T get(String key); List<T> getAll(); void dropCollection(String collectionName); }
MongoServiceImpl.java
public class MongoServiceImpl implements MongoService<Person> { protected String connectionURL = "localhost:27017"; public static final String collectionName = "persons"; protected DBCollectiom collection; protcted DB; public MongoService() { db = MongoConnectionManager.connect(connectionURL); collection = db.getCollection(collectionName); } //.... }
Below is the implementation of save() method:
public Object save(Person p) { //first extract Address from person and build the corresponding BasicDBObject BasicDBObject addrObj = new BasicDBObject(); Address addr = p.getAddress(); addrObj.append("houseNo", addr.getHouseNo()); addrObj.append("street", addr.getStreet()); addrObj.append("city", addr.getCity()); //.... //Now build the BasicDBObject for Person BasicDBObject personObj = new BasicDBObject(); personObj.append("firstName", t.getFirstName()); personObj.append("lastName", t.getLastName()); personObj.append("address", addrObj); //Note how addrObj is mapped here //finally save the personObj to the MongoDB database collection.insert(personObj); return personObj.get("_id"); }
To save a Person
instance having an embedded Address
object we have to create two BasicDBObject
instances and use hard coded keys to populate them. A call to insert(DBObject o)
method of DBCollection
class to store the person instance in the database. Finally, we simply retrieve the persisted document id using the get(String key)
method of BasicDBObject
and passed "_id"
to retrieve it. The retrieved id can be used to query the persisted object.
Let’s retrieve the persisted MongoDB document from the database by querying the peristed id.
public Person get(String id) { BasicDBObject persistent = (BasicDBObject)collection.findOne(new ObjectId(id)); //The retrieved object is a BasicDBObject. Extract values from this object and //build a person object. Person person = new Person(); Set<String> keySet = persistent.keySet(); for(String key: keySet) { Object value = persistent.get(key); buildPerson(key, value, person); } return person; }
Limitations
Its evident from the above example that saving and retrieving a document using the DBObject
API is very daunting. We can easily lose track of POJO attributes that serves as keys to the MongoDB document. For any project that has a number of POJOs, saving and retrieving objects using DBObject
API will be very frustrating. Java Mongo Driver is a poor man’s tool for performing database operations.
Using JSON Jackson along with Mongo Java Driver – The Jongo Way
Using bare bone DBObject API can be frustrating. However, a combination of JSON Jackson and Mongo Java Driver can ease the storage and retrieval of POJOs. Moreover, the embedded POJOs don’t have to be dealt separately while persisting and retrieval. The following piece of code makes use of byte[]
to marshall and unmarshall Java objects.
public Object save(Object o) throws IOException { ObjectWriter writer = mapper.writer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); writer.writeValue(baos, o); DBObject dbObject = new LazyWriteableDBObject(baos.toByteArray(), new LazyBSONCallback()); DBObject result = new BasicDBObject(); result.putAll(dbObject); collection.insert(result); return result.get("_id"); }
The save(Object o)
method use ByteArrayOutputStream
to create a DBObject
. The DBObject
is then persisted. In this case, we don’t have to create separate DBObject
instances for Container POJO and embedded POJO. The complete object graph is first converted to ByteArrayOutputStream
and then persisted.
public static <T> T getPojo(DBObject o, Class<T> clazz) throws IOException { ObjectReader reader = mapper.reader(clazz); DBEncoder dbEncoder = DefaultDBEncoder.FACTORY.create(); OutputBuffer buffer = new BasicOutputBuffer(); dbEncoder.writeObject(buffer, o); T pojo = reader.readValue(buffer.toByteArray()); return pojo; }
The getPojo()
method converts a given DBObject
to the required class type. The JSON Jackson ObjectReader
converts a given DBObject
to the POJO type.
Jongo makes use of the Jackson JSON API and Mongo Driver. Instead of converting results into strings and unmarshalling strings into objects, Jongo use byte[]
for query marshalling and unmarshalling. Jongo also provides Aggregation f/w to work with MongoDB Aggregations. More on Jongo…