X

TOTD #166: Using NoSQL database in your Java EE 6 Applications on GlassFish - MongoDB for now!

Guest Author
The Java EE 6 platform includes Java Persistence API to work with
RDBMS. The JPA specification defines a comprehensive API that includes,
but not restricted to, how a database table can be mapped to a POJO and
vice versa, provides mechanisms how a PersistenceContext can be
injected in a @Stateless bean and then be used for performing different
operations on the database table and write typesafe queries.

There are
several well known advantages of RDBMS but the NoSQL movement has
gained traction over past couple of years. The NoSQL databases
are not intended to be a replacement for the mainstream RDBMS. As href="http://www.strozzi.it/cgi-bin/CSA/tw7/I/en_US/NoSQL/Philosophy%20of%20NoSQL">Philosophy
of NoSQL explains, NoSQL database was designed for casual use where
all the features typically provided by an RDBMS are not required. The
name "NoSQL" is more of a category of databases that is more known for
what it is not rather than what it is.

The basic principles of NoSQL database are:

  1. No
    need to have a pre-defined schema and that makes them a
    schema-less database. Addition of new properties to existing
    objects is easy and does not require ALTER TABLE. The unstructured data
    gives flexibility to
    change the format of data any time without downtime or reduced service
    levels. Also there are no joins happening on the server because there
    is no structure and thus no relation between them.

  2. Scalability and performance is more important than the entire set
    of functionality typically
    provided by an RDBMS. This set of databases provide eventual
    consistency and/or transactions restricted to single
    items but more focus on CRUD.
  3. Not be restricted to SQL to access the information stored in the
    backing database.
  4. Designed to scale-out (horizontal) instead of scale-up
    (vertical). This
    is important knowing
    that databases, and everything else as well, is moving into the cloud.
    RBDMS can scale-out using sharding but requires complex management and
    not for the faint of heart.
  5. Unlike RBDMS which require a separate caching tier, most of the
    NoSQL databases comes with integrated caching.
  6. Designed for less management and simpler data models lead to
    lower administration as well.


There are primarily three types of NoSQL databases:

  1. Key-Value stores (e.g. Cassandra and Riak)
  2. Document databases (MongoDB or CouchDB)
  3. Graph databases (Neo4J)


You may think NoSQL is panacea but as I mentioned above they are not
meant to replace the mainstream databases and here is why:
  • RDBMS have been around for many years, very stable, and
    functionally rich. This is something CIOs and CTOs can bet their money
    on without much worry. There is a reason 98% of Fortune 100 companies
    run Oracle :-) NoSQL is cutting edge, brings excitement to developers,
    but enterprises are cautious about them.
  • Commercial databases like Oracle are well supported by the
    backing enterprises in terms of providing support resources on a global
    scale. There is a full ecosystem built around these commercial
    databases
    providing training, performance tuning, architecture guidance, and
    everything else. NoSQL is fairly new and typically backed by a single
    company not able to meet the scale of these big enterprises.

  • NoSQL databases are good for CRUDing operations but business
    intelligence is extremely important for enterprises to stay
    competitive. RDBMS provide extensive tooling to generate this data but
    that was not the original intention of NoSQL databases and is lacking
    in that area. Generating any meaningful information other than CRUDing
    require extensive programming.

  • Not suited for complex transactions such as banking systems or
    other highly transactional applications requiring 2-phase commit.

  • SQL cannot be used with NoSQL databases and writing simple
    queries can be involving.


Enough talking, lets take a look at some code.





This blog has published
multiple blogs on how to access a RDBMS using JPA in a Java EE 6
application. This Tip style="font-weight: bold;">Of The
Day (TOTD) will show you can
use MongoDB (a document-oriented database) with a typical 3-tier Java
EE 6 application.

Lets get started! The complete source code of this project can be downloaded here.
  1. Download MongoDB for your platform href="http://www.mongodb.org/downloads">from here (1.8.2 as of
    this writing) and start
    the server as:

    arun@ArunUbuntu:~/tools/mongodb-linux-x86_64-1.8.2/bin$style="font-weight: bold;">./mongod
    ./mongod --help for help and startup options
    Sun Jun 26 20:41:11 [initandlisten] MongoDB starting : pid=11210
    port=27017 dbpath=/data/db/ 64-bit
    Sun Jun 26 20:41:11 [initandlisten] db version v1.8.2, pdfile version
    4.5
    Sun Jun 26 20:41:11 [initandlisten] git version:
    433bbaa14aaba6860da15bd4de8edf600f56501b
    Sun Jun 26 20:41:11 [initandlisten] build sys info: Linux
    bs-linux64.10gen.cc 2.6.21.7-2.ec2.v1.2.fc8xen #1 SMP Fri Nov 20
    17:48:28 EST 2009 x86_64 BOOST_LIB_VERSION=1_41
    Sun Jun 26 20:41:11 [initandlisten] waiting for connections on port 27017
    Sun Jun 26 20:41:11 [websvr] web admin interface listening on port 28017



    The default directory for the database is /data/db and needs to be
    created as:

    sudo mkdir -p /data/db/
    sudo chown `id -u` /data/db


    You can specify a different directory using "--dbpath" option. Refer to
    Quickstart
    for your specific platform.

  2. Using NetBeans, create a Java EE 6 project and make sure to
    enable
    CDI and add JavaServer Faces framework.
  3. Download href="https://github.com/mongodb/mongo-java-driver/downloads">MongoDB
    Java Driver (2.6.3 of this writing) and add it to the project
    library by
    selecting "Properties", "LIbraries", "Add Library...", creating a new
    library by specifying the location of the JAR file, and adding the
    library to the created project.

  4. Edit the generated "index.xhtml" such that it looks like:

    <h1>Add a new movie</h1>
    <h:form>
    Name: <h:inputText value="#{movie.name}" size="20"/><br/>
    Year: <h:inputText value="#{movie.year}" size="6"/><br/>
    Language: <h:inputText value="#{movie.language}" size="20"/><br/>
    <h:commandButton actionListener="#{movieSessionBean.createMovie}"
    action="show"
    title="Add"
    value="submit"/>
    </h:form>

    This page has a simple HTML form with three text boxes and a submit
    button. The text boxes take name, year, and language of a movie and the
    submit button invokes the "createMovie" method of "movieSessionBean"
    and then render "show.xhtml".

  5. Create "show.xhtml" ("New" -> "Other..." -> "Other" ->
    "XHTML File") such that it looks like:
     <head>
    <title><h1>List of movies</h1></title>
    </head>
    <body>
    <h:form>
    <h:dataTable value="#{movieSessionBean.movies}" var="m" >
    <h:column><f:facet name="header">Name</f:facet>#{m.name}</h:column>
    <h:column><f:facet name="header">Year</f:facet>#{m.year}</h:column>
    <h:column><f:facet name="header">Language</f:facet>#{m.language}</h:column>
    </h:dataTable>
    </h:form>


    This page shows the name, year, and language of all movies stored in
    the
    database so far. The list of movies is returned by
    "movieSessionBean.movies"
    property.

  6. Now create the "Movie" class such that it looks like:

    import com.mongodb.BasicDBObject;
    import com.mongodb.BasicDBObject;
    import com.mongodb.DBObject;
    import javax.enterprise.inject.Model;
    import javax.validation.constraints.Size;

    /**
    * @author arun
    */
    @Model
    public class Movie {
    @Size(min=1, max=20)
    private String name;

    @Size(min=1, max=20)
    private String language;

    private int year;

    // getters and setters for "name", "year", "language"

    public BasicDBObject toDBObject() {
    BasicDBObject doc = new BasicDBObject();

    doc.put("name", name);
    doc.put("year", year);
    doc.put("language", language);

    return doc;
    }

    public static Movie fromDBObject(DBObject doc) {
    Movie m = new Movie();

    m.name = (String)doc.get("name");
    m.year = (int)doc.get("year");
    m.language = (String)doc.get("language");

    return m;
    }

    @Override
    public String toString() {
    return name + ", " + year + ", " + language;
    }
    }


    Other than the usual boilerplate code, the key methods here are
    "toDBObject" and "fromDBObject". These methods provide a conversion
    from "Movie" -> "DBObject" and vice versa. The "DBObject" is a
    MongoDB class that comes as part of the mongo-2.6.3.jar file and which
    we added to our project earlier.  The complete javadoc for 2.6.3
    can be seen here.
    Notice, this class also uses Bean Validation constraints and will be
    honored by the JSF layer.

  7. Finally, create "MovieSessionBean" stateless EJB with all the
    business logic such that it
    looks like:
    package org.glassfish.samples;

    import com.mongodb.BasicDBObject;
    import com.mongodb.DB;
    import com.mongodb.DBCollection;
    import com.mongodb.DBCursor;
    import com.mongodb.DBObject;
    import com.mongodb.Mongo;
    import java.net.UnknownHostException;
    import java.util.ArrayList;
    import java.util.List;
    import javax.annotation.PostConstruct;
    import javax.ejb.Stateless;
    import javax.inject.Inject;
    import javax.inject.Named;

    /**
    * @author arun
    */
    @Stateless
    @Named
    public class MovieSessionBean {

    @Inject Movie movie;

    DBCollection movieColl;

    @PostConstruct
    private void initDB() throws UnknownHostException {
    Mongo m = new Mongo();
    DB db = m.getDB("movieDB");
    movieColl = db.getCollection("movies");
    if (movieColl == null) {
    movieColl = db.createCollection("movies", null);
    }
    }

    public void createMovie() {
    BasicDBObject doc = movie.toDBObject();
    movieColl.insert(doc);
    }

    public List<Movie> getMovies() {
    List<Movie> movies = new ArrayList();
    DBCursor cur = movieColl.find();
    System.out.println("getMovies: Found " + cur.size() + " movie(s)");
    for (DBObject dbo : cur.toArray()) {
    movies.add(Movie.fromDBObject(dbo));
    }

    return movies;
    }
    }



    The database is initialized in @PostConstruct. Instead of a working
    with a database table, NoSQL databases work with a
    schema-less document. The "Movie" class is the document in our case and
    stored in the collection "movies". The collection allows us to perform
    query functions on all movies. The "getMovies" method invokes "find"
    method on the collection which is equivalent to the SQL query "select *
    from movies" and then returns a List<Movie>.



    Also notice that there is no "persistence.xml" in the project.

  8. Right-click and run the project to see the output as:



    style="width: 353px; height: 233px;" alt=""
    src="//cdn.app.compendium.com/uploads/user/e7c690e8-6ff9-102a-ac6d-e4aebca50425/f4a5b21d-66fa-4885-92bf-c4e81c06d916/Image/279c0a100b1ed4693f92e6d19a92c803/totd166_add_a_new_movie.png">




    Enter some values in the text box and click on enter to see the result
    as:



    src="//cdn.app.compendium.com/uploads/user/e7c690e8-6ff9-102a-ac6d-e4aebca50425/f4a5b21d-66fa-4885-92bf-c4e81c06d916/Image/157d228ee4e85514b536940f63e5c711/totd166_result_single_movie.png">



    If you reached here then you've successfully used MongoDB in your Java
    EE 6 application, congratulations!



Some food for thought and further play ...
  • href="http://www.mongodb.org/display/DOCS/SQL+to+Mongo+Mapping+Chart">SQL
    to MongoDB mapping shows mapping between traditional SQL ->
    Mongo query language.
  • Tutorial
    shows fun things you can do with MongoDB.
  • Try the interactive online
    shell
     
  • The cookbook provides
    common ways of using MongoDB


In terms of this project, here are some tasks that can be tried:

  1. Encapsulate database management in a JPA persistence
    provider. Is it even worth it because the capabilities are going to be
    very different ?

  2. MongoDB uses "BSonObject" class for JSON representation, add
    @XmlRootElement on a POJO and how a compatible JSON representation can
    be generated. This will make the fromXXX and toXXX methods redundant.




Join the discussion

Comments ( 9 )
  • guest Tuesday, June 28, 2011

    Are you allowed to open up a socket from an EJB?


  • Arun Gupta Tuesday, June 28, 2011

    Nope, but whats the need to open a socket from EJB in this case ?


  • Suhail Manzoor Tuesday, June 28, 2011

    I have no need, but wouldn't the Mongo instance connect to Mongo Db on port 27107 in the @PostConstruct above? Thanks for the post Arun. I am hoping someone will take nosql drivers to the JCP to get a clean api for things. Spring Data has something along those line but it could be better.


  • guest Wednesday, June 29, 2011

    Fail. The session bean will habe to open a TCP connection to the mongo server to communication. You'd better bind the mongo connection to a jndi name and use a resource provider.


  • Arun Gupta Wednesday, June 29, 2011

    Suhail, guest,

    Did you try it and it did not work ?

    I posted this tip after the code above worked in my environment.


  • Suhail Manzoor Wednesday, June 29, 2011

    It worked for me Arun. Thanks.


  • guest Friday, August 5, 2011

    Can I make a JDBC datasource and Connection Pool in Glassfish for MongoDB like we do for other databases?


  • Arun Friday, August 5, 2011

    The JDBC connection pool and resource in GlassFish can currently be configured only for RDBMS. You'll need to create your own classes to manage MongoDB.


  • guest Friday, February 22, 2013

    Then what's the point having support for NoSQL in eclipselink and making a post about Java EE6 applications if you can't even define a connection via JNDI and use the EntityManager injected through @PersistenceContext?

    Everything is so broken.


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.