Skip to content

Using Kundera with Play! Framework

karthikprasad13 edited this page Oct 21, 2016 · 5 revisions

Accessing Databases with Play! Framework

Play Framework is a high velocity web framework for Java and Scala. It’s built to make application development faster and glitch free.

Play! provides a plugin for managing JDBC connection pools. So, if you’re working with relational databases, all you’ve to do is to define database connector dependencies in its build file, specify connection properties in configuration file, and voila! You’re all set to use data sources with a single line of code. You can configure as many databases as you need. This and many other Play! features are what most of the developers would ever wish for.

…and NoSQL?

However, integrating Play! with NoSQL databases is where it all ends. Any mature NoSQL plugin is yet to be developed. Reason is obvious: These databases are varied and currently lack a common API. This means plugin for all these databases need to be written separately.

There have been efforts like PlayORM. Some people have had success with usingMorphia, which nevertheless works with MongoDB only. I haven’t yet tried them but have seen users struggling with using them.

JPA to your rescue

There is a bad news. While Play! provides seamless JPA integration with relational databases, NoSQL support hasn’t arrived yet. Good news is that Kundera - An object-datastore mapping library based on JPA 2.0, can be easily integrated with Play! framework and make it possible to work like charm with databases it currently supports. This article with guide you step-by-step on setting up Play! framework on your machine, create a project and integrate with Kundera to read and write data in Cassandra, one of the most popular NoSQL database in town. However, you’re free to use database of your choice (of course from those supported by Kundera). Read on!

Setting up a Play! project

Download Play!

  1. Goto http://www.playframework.com/download and download latest Play! release (play-2.1.2.zip at the time of this writing). Unzip in a directory of your choice. We’ll refer to uncompressed folder directory as PLAY_HOME.

  2. Add PLAY_HOME folder to you PATH environment variable, so that it’s handy when you run play commands from within your project. If you’re using Bash, you can do this by simply appending m like this to user.home/.bashrc

export PATH=$PATH:/home/impadmin/play-2.1.1
  1. …and we are done. (detailed instructions are given here).

Create your project

  1. Goto to directory where you want to create your project.

  2. Create a new project named play-jpa-kundera (or any fancy name you would like to give it): create

  3. Run command “play” to enter Play! console.

  4. You’re free to use any text editor of your choice for writing source code and modifying configuration files. Eclipse settings files can be generated by running command “eclipse”:

play

  1. execute “run” command from Play! console (server is started in development mode) run

At this stage, dependency Jars specified in Build.scala and other Play dependencies are downloaded and copied to classpath.

  1. Test your application by hitting localhost:9000 in your favorite browser. broswer

  2. Congratulations! Your Play! project “play-jpa-kundera” is ready to use. Import this project in eclipse (if you like) and start playing with it. (A detailed instructions for above steps are available here)

Play! with Kundera

Add Kundera dependencies to project/Build.scala You need to add Kundera dependency jars (which, by the way are available in a maven repository) to your project. Modify your Build.Scala file under project folder. It should look like this:

import sbt._
import Keys._
import play.Project._
 
object ApplicationBuild extends Build {
 val appName = "play-jpa-kundera"
 val appVersion = "1.0-SNAPSHOT"
 
 val appDependencies = Seq(
 "com.impetus.kundera.client" % "kundera-cassandra" % "3.6",
 javaCore
 )
 
val main = play.Project(appName, appVersion, appDependencies).settings(
 //Kundera Public repositories
 ebeanEnabled := false,
 resolvers += "Kundera" at "https://oss.sonatype.org/content/repositories/releases",
 resolvers += "Riptano" at "http://mvn.riptano.com/content/repositories/public",
 resolvers += "Kundera missing" at "http://kundera.googlecode.com/svn/maven2/maven-missing-resources",
 resolvers += "Scale 7" at "https://github.com/s7/mvnrepo/raw/master"
 )
}

It’s always better to use the most recent release of Kundera.

Caution: javaJdbc app dependency downloads hibernate-entitymanager jar file that interferes with Kundera. Make sure you remove this app dependency which is by default present.

Write persistence.xml

We are now ready to build our persistence code. First step towards that is writing persistence.xml file, which should be put under conf/META-INF folder:

<persistence version="2.0" 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_2_0.xsd">
 <persistence-unit name="cassandra_pu">
 <provider>com.impetus.kundera.KunderaPersistence</provider>
 <class>model.User</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
 <properties>
 <property name="kundera.nodes" value="localhost"/>
 <property name="kundera.port" value="9160"/>
 <property name="kundera.keyspace" value="KunderaExamples"/>
 <property name="kundera.dialect" value="cassandra"/>
 <property name="kundera.client.lookup.class" value="com.impetus.client.cassandra.thrift.ThriftClientFactory" />
 </properties>
 </persistence-unit>
</persistence>

Here, model.User is entity class that we’re going to write next.

Create User Entity

Create a User entity class under app/model package:

package model;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
 
@Entity
@Table(name = "users", schema = "KunderaExamples@cassandra_pu")
public class User
{
 @Id
 private String userId;
 
 @Column(name="first_name")
 private String firstName;
 
 @Column(name="last_name")
 private String lastName;
 
 @Column(name="city")
 private String city;
 
//Getters/ setters/ constructors go here
 
}

Write UserController

Now, time to write the class where magic actually happens, UserController for persist/find/update/delete operations. Write this class under app/controller package:

package controllers;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
 
import model.User;
import play.Play;
import play.mvc.Controller;
import play.mvc.Result;
 
public class UserController extends Controller
{
 static EntityManagerFactory emf;
 
 public static Result persist()
 {
 EntityManager em = getEmf().createEntityManager();
 
User user = new User();
 user.setUserId("0001");
 user.setFirstName("John");
 user.setLastName("Smith");
 user.setCity("London");
 
em.persist(user);
 em.close();
 return ok("User 0001 record persisted for persistence unit cassandra_pu");
 }
 
public static Result find()
 {
 EntityManager em = getEmf().createEntityManager();
 User user = em.find(User.class, "0001");
 em.close();
 return ok("Found User in database with the following details:" + printUser(user));
 }
 
public static Result update()
 {
 EntityManager em = getEmf().createEntityManager();
 User user = em.find(User.class, "0001");
 user.setCity("New York");
 em.merge(user);
 user = em.find(User.class, "0001");
 return ok("Record updated:" + printUser(user));
 }
 
public static Result delete()
 {
 EntityManager em = getEmf().createEntityManager();
 User user = em.find(User.class, "0001");
 
em.remove(user);
 user = em.find(User.class, "0001");
 return ok("Record deleted:" + printUser(user));
 }
 
private static EntityManagerFactory getEmf()
 {
 if (emf == null)
 {
  emf = Persistence.createEntityManagerFactory("cassandra_pu");
 }
 return emf;
 }
 
private static String printUser(User user)
 {
 if (user == null)
 return "Record not found";
 
return "\n--------------------------------------------------" + "\nuserId:" + user.getUserId() + "\nfirstName:"
 + user.getFirstName() + "\nlastName:" + user.getLastName() + "\ncity:" + user.getCity();
 }
}

Add controller methods to conf/routes file

We’ll add one URL path for each one of CRUD methods in controller. Add them to your conf/routes file, It should look like this:

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
 
# Map static resources from the /public folder to the /assets URL path
 
GET /assets/*file controllers.Assets.at(path="/public", file)
 
################# User Controller###############
 
# Persist Operation
GET /persist controllers.UserController.persist()
 
# Find Operation
GET /find controllers.UserController.find()
 
# Update Operation
GET /update controllers.UserController.update()
 
# Delete Operation
GET /delete controllers.UserController.delete()

Make entity classes available at runtime

You can skip this step if you’re running Play! in production mode (by running “play start”). However, If you’re running Play! in development mode, Play! uses its own Classloader that doesn’t read classes from target/scala-2.xx/classes folder. As a result, your User entity won’t be loaded at runtime and Kundera will throw below exception when you try to run application:

com.impetus.kundera.KunderaException: com.impetus.kundera.KunderaException: EntityMetadata should not be null
at com.impetus.kundera.persistence.EntityManagerImpl.persist(EntityManagerImpl.java:174)

In order to make your entity classes available at run time (in development mode, of course), all you’ve to do is to create jar file out of them and put this jar file into lib folder under project root directory. (create lib folder as it won’t exist by default) entities

Start Cassandra Server and create schema/ tables

I am assuming you’ve already Cassandra server (or database server you’re working on) setup and running on your machine. If not, refer to their respective documentation.

Now, create schema and tables for User entity to be written to:

impadmin@impetus-D189a:/usr/local/apache-cassandra-1.1.6/bin$ ./cassandra-cli --host localhost --port 9160
Connected to: "Test Cluster" on localhost/9160
Welcome to the Cassandra CLI.
Type 'help;' or '?' for help.
Type 'quit;' or 'exit;' to quit.
[default@unknown]create keyspace KunderaExamples;
[default@unknown]use KunderaExamples;
[default@KunderaExamples]create column family users with comparator=UTF8Type and default_validation_class=UTF8Type and key_validation_class=UTF8Type;
81852270-2374-11e1-0000-242d50cf1fdd
Waiting for schema agreement...
... schemas agree across the cluster

Test your application

Now, time to fasten your seat-belts! Run below URLs in your web browser one after other:

  1. http://localhost:9000/persist persist

You can test inserted data from Cassandra-cli Cli

  1. http://localhost:9000/find find

  2. http://localhost:9000/update update

  3. http://localhost:9000/delete delete

Clone this wiki locally