How to connect to MongoDB from a Java EE stateless application
In this post I will present how to connect to MongoDB from a stateless Java EE application, to take advantage of the built-in pool of connections to the database offered by the MongoDB Java Driver. This might be the case if you develop a REST API, that executes operations against a MongoDB.
Get the Java MongoDb Driver
To connect from Java to MongoDB, you can use the Java MongoDB Driver. If you are building your application with Maven, you can add the dependency to the pom.xml file:
MongoDB java driver dependency
org.mongodb mongo-java-driver 2.12.3
The driver provides a MongoDB client (com.mongodb.MongoClient) with internal pooling. The MongoClient class is designed to be thread safe and shared among threads. For most applications, you should have one MongoClient instace for the entire JVM. Because of that you wouldn’t want create a new MongoClient instace with each request in your Java EE stateless application.
Implement a @Singleton EJB
A simple solution is to use a @Singleton EJB to hold the MongoClient:
Singleton to hold the MongoClient
package org.codingpedia.demo.mongoconnection; import java.net.UnknownHostException; import javax.annotation.PostConstruct; import javax.ejb.ConcurrencyManagement; import javax.ejb.ConcurrencyManagementType; import javax.ejb.Lock; import javax.ejb.LockType; import javax.ejb.Singleton; import com.mongodb.MongoClient; @Singleton @ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) public class MongoClientProvider { private MongoClient mongoClient = null; @Lock(LockType.READ) public MongoClient getMongoClient(){ return mongoClient; } @PostConstruct public void init() { String mongoIpAddress = "x.x.x.x"; Integer mongoPort = 11000; try { mongoClient = new MongoClient(mongoIpAddress, mongoPort); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Note:
@Singleton
– probably the most important line of code in this class. This annotation specifies that there will be exactly one singleton of this type of bean in the application. This bean can be invoked concurrently by multiple threads. It comes also with a@PostConstruct
annotation. This annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization – in our case is to initialize the MongoClient- the
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
declares a singleton session bean’s concurrency management type. By default it is set toContainer,
I use it here only to highlight its existence. The other optionConcurrencyManagementType.BEAN
specifies that the bean developer is responsible for managing concurrent access to the bean instance. - the
@Lock(LockType.READ)
specifies the concurrency lock type for singleton beans with container-managed concurrency. When set toLockType.READ
, it enforces the method to permit full concurrent access to it (assuming no write locks are held). This permits several threads to access the same MongoClient instance and take advantage of the internal pool of connections to the database. This is VERY IMPORTANT, because the other more conservative option@Lock(LockType.WRITE)
, is the DEFAULT and enforces exclusive access to the bean instance. This should make the method slower in a highly concurrent environment…
Use the @Singleton EJB
Now that you have the MongoClient “persisted” in the application, you can inject the MongoClientProvider to access the MongoDB (to get the collection names for example):
Access MongoClient from other beans example
package org.codingpedia.demo.mongoconnection; import java.util.Set; import javax.ejb.EJB; import javax.ejb.Stateless; import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBObject; import com.mongodb.MongoClient; import com.mongodb.util.JSON; @Stateless public class TestMongoClientProvider { @EJB MongoClientProvider mongoClientProvider; public Set<String> getCollectionNames(){ MongoClient mongoClient = mongoClientProvider.getMongoClient(); DB db = mongoClient.getDB("myMongoDB"); Set<String> colls = db.getCollectionNames(); for (String s : colls) { System.out.println(s); } return colls; } }
Note: The db object will be a connection to a MongoDB server for the specified database. With it, you can do further operations. I encourage you to read the Getting Started with Java Driver for more on that…
Be aware
One aspect to bear mind:
“For every request to the DB (find, insert, etc) the Java thread will obtain a connection from the pool, execute the operation, and release the connection. This means the connection (socket) used may be different each time.
Additionally in the case of a replica set with slaveOk option turned on, the read operations will be distributed evenly across all slaves. This means that within the same thread, a write followed by a read may be sent to different servers (master then slave). In turn the read operation may not see the data just written since replication is asynchronous. If you want to ensure complete consistency in a “session” (maybe an http request), you would want the driver to use the same socket, which you can achieve by using a “consistent request”. Call requestStart() before your operations and requestDone() to release the connection back to the pool:
Ensuring complete consistency in a
DB db...; db.requestStart(); try { db.requestEnsureConnection(); code.... } finally { db.requestDone(); }
DB
and DBCollection
are completely thread safe. In fact, they are cached so you get the same instance no matter what.” [3]
Resources
- Java MongoDB Driver
- Getting Started with Java Driver
- Java Driver Concurrency
- GitHub – mongodb / mongo-java-driver examples
Reference: | How to connect to MongoDB from a Java EE stateless application from our JCG partner Adrian Matei at the Codingpedia.org blog. |