18 October 2015

This is a quick step-by-step tutorial on using Grails 2.4.4 with MongoDB Grails plugin for newbies like me in your Grails application.

MongoDB GORM is a plugin that integrates the Mongo document datastore into Grails, providing a GORM(Object Relational Mapping) API onto it.

Install and set-up JAVA & Grails on an Ubuntu 14.04

You will need to setup Java and Grails on Ubuntu. This blog really documents it well.

Install MongoDB

Intall MongoDB on your box. Here is the official documentation on how to do it and have the mongo service up and running.

DB creation

  1. To enter mongodb shell run the following command
    
    $mongo
    
    
    This will connect you to running mongod instance.
  2. MongoDB Help: To get list of commands type db.help() in mongodb client. This will give you list of commands as follows:
    
    > db.help()
    DB methods:
    	db.adminCommand(nameOrDocument) - switches to 'admin' db, and runs command [ just calls db.runCommand(...) ]
    	db.auth(username, password)
    	db.cloneDatabase(fromhost)
    	db.commandHelp(name) returns the help for the command
    	db.copyDatabase(fromdb, todb, fromhost)
    	db.createCollection(name, { size : ..., capped : ..., max : ... } )
    	db.createUser(userDocument)
    	db.currentOp() displays currently executing operations in the db
    	db.dropDatabase()
    	db.eval() - deprecated
    	db.fsyncLock() flush data to disk and lock server for backups
    	db.fsyncUnlock() unlocks server following a db.fsyncLock()
    	db.getCollection(cname) same as db['cname'] or db.cname
    	db.getCollectionInfos()
    	db.getCollectionNames()
    	db.getLastError() - just returns the err msg string
    	db.getLastErrorObj() - return full status object
    	db.getLogComponents()
    	db.getMongo() get the server connection object
    	db.getMongo().setSlaveOk() allow queries on a replication slave server
    	db.getName()
    	db.getPrevError()
    	db.getProfilingLevel() - deprecated
    	db.getProfilingStatus() - returns if profiling is on and slow threshold
    	db.getReplicationInfo()
    	db.getSiblingDB(name) get the db at the same server as this one
    	db.getWriteConcern() - returns the write concern used for any operations on this db, inherited from server object if set
    	db.hostInfo() get details about the server host
    	db.isMaster() check replica primary status
    	db.killOp(opid) kills the current operation in the db
    	db.listCommands() lists all the db commands
    	db.loadServerScripts() loads all the scripts in db.system.js
    	db.logout()
    	db.printCollectionStats()
    	db.printReplicationInfo()
    	db.printShardingStatus()
    	db.printSlaveReplicationInfo()
    	db.dropUser(username)
    	db.repairDatabase()
    	db.resetError()
    	db.runCommand(cmdObj) run a database command.  if cmdObj is a string, turns it into { cmdObj : 1 }
    	db.serverStatus()
    	db.setLogLevel(level,)
    	db.setProfilingLevel(level,) 0=off 1=slow 2=all
    	db.setWriteConcern(  ) - sets the write concern for writes to the db
    	db.unsetWriteConcern(  ) - unsets the write concern for writes to the db
    	db.setVerboseShell(flag) display extra information in shell output
    	db.shutdownServer()
    	db.stats()
    	db.version() current version of the server
    
    
  3. MongoDB Statistics: To get stats about mongodb server type the command db.stats() in mongodb client. This will show the database name, number of collections and documents in the database.If you are unfamilar with what document and collection is, please refer the documention. Output of the command is shown below:
    
    > db.stats()
    {
    	"db" : "test",
    	"collections" : 0,
    	"objects" : 0,
    	"avgObjSize" : 0,
    	"dataSize" : 0,
    	"storageSize" : 0,
    	"numExtents" : 0,
    	"indexes" : 0,
    	"indexSize" : 0,
    	"fileSize" : 0,
    	"ok" : 1
    }
    
    
  4. To check your current database use the command db
    
    >db
    test
    
    
    In mongodb default database is test. If you didn't create any database then collections will be stored in test database.
  5. If you want to create a database with name , then use DATABASE statement as follows:
    
    >use University
    switched to db University
    
    
    To check your currently selected database use the command db
    
    >db
    University
    
    
  6. If you want to check your databases list, then use the command show dbs
    
    >show dbs
    local     0.78125GB
    test      0.23012GB
    
    
  7. Your created database (University) is not present in list. To display database you need to insert atleast one document into it.
    
    >db.Student.insert({"name":"Test Student"})
    >show dbs
    local            0.78125GB
    University       0.23012GB
    test             0.23012GB
    
    

App creation

  1. Create grails app in intelij. Please find this documented here.
  2. To get started with GORM for Mongo you need configure it as a dependency in BuildConfig.groovy:
    
    plugins {
    	compile ':mongodb:3.0.3' // or whatever is the latest vesrion
    }
    
    
    If you plan to use MongoDB as your primary datastore then you need to remove the Hibernate plugin from the grails-app/conf/BuildConfig.groovy file by commenting out the hibernate line in the plugins block
    
    compile ':hibernate:3.6.10.12'
    
    
  3. As you can see the server is running on port 27017 (check mongod.log, I love this file.), but don't worry. The Mongodb plugin for Grails will automatically configure itself to look for Mongodb on that port by default. If you want to configure how Grails connects to Mongo then you can do so using the following settings in grails-app/conf/DataSource.groovy:
    
    grails {
        mongo {
            host = "localhost"
            port = 27017
            username = "DemoUser"
            password = "password"
            databaseName = "University"
        }
    }
    
    
    But easy way (and worked for me) is "connectionString". You can remove all content from DataSource.groovy which was added previously and replace it with the following:
    
    grails {
        mongo {
            connectionString="mongodb://DemoUser:password@localhost:27017/University"
        }
    }
    
    
    The following is the standard URI connection scheme:
    
    mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
    
    
    Using MongoDB connection strings is currently the most flexible and recommended way to configure MongoDB connections.
  4. Global Mapping Configuration

    Using the grails.mongo.default.mapping setting in Config.groovy you can configure global mapping options across your domain classes. This is useful if, for example, you want to disable optimistic locking globally or you wish to use DBRefs in your association mappings. For example, the following configuration will disable optimistic locking globally and use DBRefs for all properties:
    
    grails.mongo.default.mapping = {
        version false
        '*'(reference:true)
    }
    
    
    The * method is used to indicate that the setting applies to all properties.
  5. Basic Mapping

    The way GORM for Mongo works is to map each domain class to a Mongo collection. For example given a domain class such as:
    
    class Student {
        String name
        static constraints = {
            name attr:"name"
        }
    }
    
    
    This will map onto a Mongo DBCollection called "Student".

    After implementing step 6 you may hit the following error in IntellIJ Run window:

    
    Error 2015-10-18 22:37:45,514 [http-bio-9091-exec-10] ERROR errors.GrailsExceptionResolver  - ConverterNotFoundException occurred when processing request: [GET] /UniversityApp/student/index
    No converter found capable of converting from type org.bson.types.ObjectId to type java.lang.Long. Stacktrace follows:
    Message: No converter found capable of converting from type org.bson.types.ObjectId to type java.lang.Long
    To explain more, MongoDB has a default _id (Int) as primary key for a document (Row in sql) in a collection (Table in sql). Grails will try to map Long ID of domain class Student with Int _id of document Student. It may give the following error in browser at runtime:
    
    Class:org.springframework.core.convert.ConverterNotFoundException
    Message:No converter found capable of converting from type org.bson.types.ObjectId to type java.lang.Long
    To solve it, you will need to add one more field as "ObjectId id" in domain class Student. Your updated domain class shall look like this:
    
    import org.bson.types.ObjectId
    
    class Student {
        ObjectId id
        String name
        static constraints = {
            name attr:"name"
        }
    }
    
    
  6. Generate controller with scaffolding.

    
    class StudentController {
    static scaffold = Student
    }
    
    
    Or Generate controller as well as views.
  7. Run application

    For running your application you have to ensure that mogod service is running by issuing the following command
    
    $ mongostat
    
    
    if you get this message “couldn’t connect to [127.0.0.1] couldn’t connect to server 127.0.0.1” you must start the mongod service. See how to start mongod but if the mongo service is already running you will see the result of that command like this

     

     

    After everything goes OK It’s time to run-app
    
    $ grails run-app
    
    
    and hit the browser by localhost:8080/yourapp you will see
  8. Test insert and query data

  9. Use mongoDB shell to see data

    
    $ mongo
    MongoDB shell version: 3.0.7
    connecting to: test
    > use University
    switched to db University
    > show collections
    Student
    Student.next_id
    system.indexes
    > db.Student.find()
    { "_id" : ObjectId("5623a53844ae4ef024a71201"), "name" : "Roy", "version" : NumberLong(1) }
    { "_id" : ObjectId("5623a53e44ae4ef024a71202"), "name" : "Molly", "version" : NumberLong(1) }
    
    

Conclusion

We configured a grails application to connect to Mongo DB using the Grails plugin. All the internal nuances of making grails to talk to mongo are described in the most basic way. I have tried to document all the basic quirks I faced during making the configuration to work. I hope this helps somone. Please feel free to comment, subscribe and share the blog.