Working with MongoDB using Kundera
Kundera – a JPA 2.0 (Java Persistence API) based ORM library was originally developed for Cassandra database. A support for Hbase was later added to it.
Those who are new to Kundera, should read this to get a basic idea. For impatient ones – Get started with Kundera in 5 minutes!
Kundera now supports MongoDB – a document-oriented database. Some salient features of MongoDB datamodel are:
- Documents are stored as “collections” in DBs.
- A document can be embedded into other document, and hence no join is needed for retrieving data. This makes database operations fast.
- Drivers are available for most of the popular programming languages.
Our Data Model
{
EMAIL:
{
“MESSAGE_ID” : “1″,
“SUBJECT” : “Please Join Meeting”,
“BODY” : “Agenda: RFP discussion”,
“FROM” :
{
“CONTACT_ID” : “a″,
“FIRST_NAME” : “Amresh”,
“LAST_NAME” : “Singh”,
“EMAIL_ID” : “xamry@gmail.com”
}
“TO” : [
{
“CONTACT_ID” : “b″,
“FIRST_NAME” : “Vivek”,
“LAST_NAME” : “Mishra”,
“EMAIL_ID” : “mevivs@gmail.com”
},
{
“CONTACT_ID” : “c″,
“FIRST_NAME” : “Saurabh”,
“LAST_NAME” : “Singh”,
“EMAIL_ID” : “saurabh@gmail.com”
}
]
}
}
{
ATTACHMENT:
{
"ATTCHMENT_ID" : "aaa",
"FILE_NAME" : "Agenda.doc",
"FILE_TYPE" : "MS Word",
"MESSAGE_ID" : "1"
}
{
"ATTCHMENT_ID" : "bbb",
"FILE_NAME" : "MOM_Last_Meeting.xls",
"FILE_TYPE" : "MS Excel",
"MESSAGE_ID" : "1"
}
{
"ATTCHMENT_ID" : "ccc",
"FILE_NAME" : "Client_Feedback.txt",
"FILE_TYPE" : "Text",
"MESSAGE_ID" : "1"
}
}
Environment Setup
Create Entity Classes
- Your entity classes should be annotated with @Entity and each fields should be annotated with @Column.
- Field corresponding to primary key should be annotated with @Id.
- In addition to this, your entity class should also be annotated with @Table, which is used by Kundera to determine the DB name and collection name.
- All embedded entity fields should be annotated with @Embedded or @ElementCollection. They are stored with the enclosing entity table
- All Relationships should be annotated with @OneToOne, @OneToMany, @ManyToOne or @ManyToMany. They are stored as separate collection and relationship is maintained via either Join Column or Join Table as the case may be.
Email Entity
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "EMAIL", schema = "KunderaExamples@twingo")
public class Email
{
@Id
@Column(name="MESSAGE_ID")
private String messageId;
@Column(name = "SUBJECT")
private String subject;
@Column(name = "BODY")
private String body;
@Embedded
private Contact from;
@ElementCollection
@CollectionTable(name = "TO")
private List<Contact> to;
@OneToMany (cascade={CascadeType.ALL}, fetch=FetchType.LAZY)
@JoinColumn(name="MESSAGE_ID")
private List<Attachment> attachments;
//Getters, setters, constructors and utility methods omitted.
}
Contact Embedded Object
import javax.persistence.Column;
import javax.persistence.Embeddable;
@Embeddable
public class Contact
{
@Column(name = "CONTACT_ID")
String contactId;
@Column(name = "LAST_NAME")
String lastName;
@Column(name = "FIRST_NAME")
String firstName;
@Column(name = "EMAIL_ID")
String emailId;
//Getters, setters, constructors and utility methods omitted.
}
Attachment Entity
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "ATTACHMENT", schema = "KunderaExamples@twingo")
public class Attachment
{
@Id
@Column(name="ATTCHMENT_ID")
private String attachmentId;
@Column(name = "FILE_NAME")
private String fileName;
@Column(name = "FILE_TYPE")
private String fileType;
//Getters, setters, constructors and utility methods omitted.
}
Create persistence.xml
Add the below entry in your persistence.xml file for letting kundera know the persistence unit name and details. This name is used in the application code for making connection with MongoDB and persisting entities.
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="2.0">
<persistence-unit name="twingo">
<provider>com.impetus.kundera.KunderaPersistence</provider>
<properties>
<property name="kundera.nodes" value="localhost" />
<property name="kundera.port" value="27017" />
<property name="kundera.keyspace" value="KunderaExamples" />
<property name="kundera.dialect" value="mongodb" />
<property name="kundera.client.lookup.class" value="com.impetus.client.mongodb.MongoDBClientFactory" />
<property name="kundera.cache.provider.class" value="com.impetus.kundera.cache.ehcache.EhCacheProvider" />
<property name="kundera.cache.config.resource" value="/ehcache-test.xml" />
</properties>
</persistence-unit>
</persistence>
Performing CRUD operations
- Create instance of EntityManagerFactory, providing Persistence Unit name as parameter.
- Perform database operation using JPA 2.0 API.
- Cleanup resources.
Insert a new entity:
Email email = new Email();
email.setMessageId("1");
email.setSubject("Please Join Meeting");
email.setBody("Agenda: RFP discussion");
email.setFrom(new Contact("a", "Amresh", "Singh", "xamry@gmail.com"));
email.addTo(new Contact("b", "Vivek", "Mishra", "mevivs@gmail.com"));
email.addTo(new Contact("c", "Saurabh", "Singh", "saurabh@gmail.com"));
email.addAttachment(new Attachment("aaa", "Agenda.doc", "MS Word"));
email.addAttachment(new Attachment("bbb", "MOM_Last_Meeting.xls", "MS Excel"));
email.addAttachment(new Attachment("ccc", "Client_Feedback.txt", "Text"));
EntityManagerFactory emf = Persistence.createEntityManagerFactory("twingo");
EntityManager em = emf.createEntityManager();
em.persist(email);
em.close();
emf.close();
You can verify whether entity was successfully persisted from the mongodb shell:
amresh@ubuntu:/usr/local/mongodb$ ./bin/mongo
MongoDB shell version: 1.8.1
connecting to: test
> use KunderaExamples;
switched to db KunderaExamples
> show collections;
ATTACHMENT
EMAIL
system.indexes
> db.EMAIL.find();
{ "_id" : "1", "MESSAGE_ID" : "1", "subject" : { "SUBJECT" : "Please Join Meeting" }, "body" : { "BODY" : "Agenda: RFP discussion" }, "to" : [
{
"CONTACT_ID" : "b",
"LAST_NAME" : "Mishra",
"FIRST_NAME" : "Vivek",
"EMAIL_ID" : "mevivs@gmail.com"
},
{
"CONTACT_ID" : "c",
"LAST_NAME" : "Singh",
"FIRST_NAME" : "Saurabh",
"EMAIL_ID" : "saurabh@gmail.com"
}
], "from" : { "CONTACT_ID" : "a", "LAST_NAME" : "Singh", "FIRST_NAME" : "Amresh", "EMAIL_ID" : "xamry@gmail.com" } }
> db.ATTACHMENT.find();
{ "_id" : "aaa", "ATTCHMENT_ID" : "aaa", "FILE_NAME" : "Agenda.doc", "FILE_TYPE" : "MS Word", "MESSAGE_ID" : "1" }
{ "_id" : "bbb", "ATTCHMENT_ID" : "bbb", "FILE_NAME" : "MOM_Last_Meeting.xls", "FILE_TYPE" : "MS Excel", "MESSAGE_ID" : "1" }
{ "_id" : "ccc", "ATTCHMENT_ID" : "ccc", "FILE_NAME" : "Client_Feedback.txt", "FILE_TYPE" : "Text", "MESSAGE_ID" : "1" }
>
Update an existing entity:
Code for updating an entity is similar to the one used for inserting entity. You just need to set fields in the entity and persist it. It will be updated into MongoDB.
Find an entity:
Finding an entity means retrieving a single entity if you know the primary key. Here is the java code to accomplish this task:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("twingo");
EntityManager em = emf.createEntityManager();
Email email = em.find(Email.class, "1");
em.close();
emf.close();
Run a JPA Query:
Kundera supports JPA-QL. See below an example code that fetches all the emails from a collection.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("twingo");
//Get all emails
EntityManager em = emf.createEntityManager();
Query q = em.createQuery("select e from Email e");
List<?> emails = q.getResultList();
em.close();
emf.close();
Delete an Entity:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("twingo");
EntityManager em = emf.createEntityManager();
Email email = em.find(Email.class, "1");
em.remove(email);
em.close();
emf.close();
Conclusion
Kundera makes it easier to work with NoSQL databases like Cassandra, Hbase, MongoDB by supporting JPA. Programmers, as a result, need to write lesser lines of code, which is easy to understand and maintain. Another advantage is, you don’t need to rewrite your code in case you choose to change your database vendor (say, from MongoDB to Cassandra). The only thing you’ll require is to add a persistence unit entry into persistence.xml, and use that name into your application code.


Awesome!
im using jpa2-hibernate with Mysql. i want to migrate to MongoDB. do i need to downgrade my jpa to jpa1. and can you give us a pure kundera-jpa-spring tutorial, i mean no “com.impetus.kundera.loader.Configuration” stuff. i want to access the entityManager via persistanceContext, not with this “conf.getEntityManager()”.
As of now Kundera is built on JPA 1.0. Purpose behind Kundera was to make working with NoSQL database easier and fast and I don’t see much benefit in upgrading.
However, we are considering upgrading to JPA 2.0.
here is a pretty good article (https://github.com/impetus-opensource/Kundera-Examples/wiki/Using-Kundera-with-Spring) on how to use Kundera with Spring Please try this and let me know if you face any issue.
Update: Kundera is now upgraded to JPA 2.0
Kundera has undergone major revamping and a new version 2.0.1 has been released. We are coming up with frequent changes, so have an eye on Kundera home page. To get a feel of new feature, visit: https://github.com/impetus-opensource/Kundera-Examples/wiki/Twitter
i’m using mongoDB in Spring MVC application and I wanna know if there is any annotation for relationship
: one-to-one
many-to-one
one-to-many
many-to-many
i read in Spring data mongoDb document that
“There’s no need to use something like @OneToMany because the mapping framework sees that you’re wanting a one-to-many relationship because there is a List of objects. When the object is stored in MongoDB, there will be a list of DBRefs rather than the Account objects themselves.
”
so what for the other relationships?
Tks in advance
Although it’s possible to detect relationship type based on field data type as you said and as spring data does; Kundera, being a JPA based library, strictly uses these annotations for creating documents that are related. For embedding a document within another, use @Embedded or @ElementCollection annotation
Very nice post!! Its useful…
I´m using kundera-mongo with authentication, its work fine!
I hope the criteria implementation.
Thanks Karl!
Amresh – Good work. I have couple of questions though.
1. How does this compare with spring-data?
2. Why should I use this over spring data?
Hi Arvind,
I am yet to try SpringData, so I won’t be able to compare these two. Kundera maybe better placed, if polyglot persistence is a key requirement. Simplicity and Java EE familiarity is another consideration area.