DWR, Hibernate and Dojo.E - A Tutorial

Preview:

DESCRIPTION

This tutorial will walk you through the steps of creating a simple database-driven AJAX application using Direct Web Remoting (DWR) 2.0, Hibernate 3.0 and a new open source project from Nexaweb Technologies, dojo.E. dojo.E is an extensible markup processing engine that out-of-the-box will translate XML into dojo components.

Citation preview

Direct Web Remoting, Direct Web Remoting, Hibernate and Dojo.EHibernate and Dojo.E

“Easy Ajax for Java”

Joel Barciauskas

October 3, 2008

What is DWR?

• “Easy Ajax for Java” implies two parts

– Server – Java servlet

– Client – JavaScript auto-generated by the Java servlet

• Enables JSON to JavaBean serialization

• Exposes selected methods and JavaBean properties on the server as client-side JavaScript methods and objects

• Reverse Ajax – IMB-like functionality

Why use DWR? Why not?

• Pros

– Avoid replication of effort defining model objects on both server and client

– No XHR boilerplate required

– No serialization boilerplate

– JSON is fastest format to serialize/deserialize in the browser

• Cons

– Less control over network requests

E.g., harder to bundle requests

– Not RESTful, all requests processed through POST data rather than URLs

Best for single-page applications

Let’s see it

• Application: Simple database create and read

• Using: JavaScript, DWR 2.0, Hibernate 3.0, Derby

• Shell: http://source.nexaweb.com/svn/repos/trunk/tutorials/ajax/DWRExample/

Create your domain class (JavaBean)• src/events/Event.java

package events;

import java.util.Date;

public class Event {

private Long id;

private String title;

private Date date;

public Event() {}

public Long getId() { return id; }

private void setId(Long id) { this.id = id; }

public Date getDate() { return date; }

public void setDate(Date date) { this.date = date; }

public String getTitle() { return title; }

public void setTitle(String title) { this.title = title; }

}

Create Hibernate XML Mapping

• src/events/Event.hbm.xml

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="events.Event" table="EVENTS"> </class>

</hibernate-mapping>

Add Property Mappings• src/events/Event.hbm.xml

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="events.Event" table="EVENTS">

<id name="id" column="EVENT_ID">

<generator class="native"/>

</id>

<property name="date" type="timestamp" column="EVENT_DATE"/>

<property name="title"/>

</class>

</hibernate-mapping>

Create Hibernate Configuration• src/hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration> <session-factory>

<!-- Database connection settings -->

<property name="connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</property>

<property name="connection.url“>jdbc:derby:eventDB;create=true</property>

<!-- JDBC connection pool (use the built-in) -->

<property name="connection.pool_size">1</property>

<!-- SQL dialect -->

<property name="dialect">org.hibernate.dialect.DerbyDialect</property>

<!-- Enable Hibernate's automatic session context management -->

<property name="current_session_context_class">thread</property>

<!-- Disable the second-level cache -->

<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

<!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property>

<!-- Drop and re-create the database schema on startup -->

<property name="hbm2ddl.auto">create</property>

<mapping resource="events/Event.hbm.xml"/>

</session-factory>

</hibernate-configuration>

Create SessionFactory instance• src/util/HibernateUtil.java

package util;

import org.hibernate.*;

import org.hibernate.cfg.*;

public class HibernateUtil {

private static final SessionFactory sessionFactory;

static {

try { // Create the SessionFactory from hibernate.cfg.xml

sessionFactory = new Configuration().configure().buildSessionFactory();

} catch (Throwable ex) {

// Make sure you log the exception, as it might be swallowed

System.err.println("Initial SessionFactory creation failed." + ex);

throw new ExceptionInInitializerError(ex);

}

}

public static SessionFactory getSessionFactory() { return sessionFactory; }

}

Note: This can also be achieved through the Spring integration – “use the Spring OpenSessionInViewFilter which will ensure that a Hibernate Session is open” (http://directwebremoting.org/dwr/server/hibernate)

Create an Event Manager• src/events/EventManager.java

package events;

import org.hibernate.Session;

import java.util.Date;

import java.util.List;

import util.HibernateUtil;

public class EventManager {

public void createAndStoreEvent(String title, Date theDate) {

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

session.beginTransaction();

Event theEvent = new Event();

theEvent.setTitle(title);

theEvent.setDate(theDate);

session.save(theEvent);

session.getTransaction().commit();

}

public List listEvents() {

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

session.beginTransaction();

List result = session.createQuery("from Event").list();

session.getTransaction().commit();

return result;

}

}

Add the DWR servlet to web.xml• WebContent/WEB-INF/web.xml

<servlet>

<servlet-name>dwr-invoker</servlet-name>

<display-name>DWR Servlet</display-name>

<servlet-class>

org.directwebremoting.servlet.DwrServlet

</servlet-class>

<init-param>

<param-name>debug</param-name>

<param-value>true</param-value>

</init-param>

</servlet>

<servlet-mapping>

<servlet-name>dwr-invoker</servlet-name>

<url-pattern>/dwr/*</url-pattern>

</servlet-mapping>

Create a Remote Proxy and Expose Remote Methods

• Src/events/EventManager.java

package events;

import org.directwebremoting.annotations.RemoteMethod;

import org.directwebremoting.annotations.RemoteProxy;

import org.hibernate.Session;

import java.util.Date;

import java.util.List;

import util.HibernateUtil;

@RemoteProxy

public class EventManager {

@RemoteMethod

public void createAndStoreEvent(String title, String theDate) {

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

session.beginTransaction();

Event theEvent = new Event();

theEvent.setTitle(title);

theEvent.setDateString(theDate);

session.save(theEvent);

session.getTransaction().commit();

}

@RemoteMethod

public List listEvents() {

Session session = HibernateUtil.getSessionFactory().getCurrentSession();

session.beginTransaction();

List result = session.createQuery("from Event").list();

session.getTransaction().commit();

return result;

}

}

Annotate Event as a Data Transfer Object• src/events/Event.java

package events;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;

import org.directwebremoting.annotations.DataTransferObject;

import org.directwebremoting.annotations.RemoteProperty;

@DataTransferObject

public class Event {

@RemoteProperty

private Long id;

@RemoteProperty

private String title;

private Date date;

public Event() {}

public Long getId() {

return id;

}

private void setId(Long id) {

this.id = id;

}

@RemoteProperty

public String getDateString() {

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");

return sdf.format(this.date);

}

[continued on next slide]

Annotate Event as a Data Transfer Object (con’t)• src/events/Event.java [continued]

@RemoteProperty

public void setDateString(String dateStr) {

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");

try {

this.date = sdf.parse(dateStr);

} catch (ParseException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public Date getDate() {

return date;

}

public void setDate(Date date) {

this.date = date;

}

}

Note: We added getDateString and setDateString here, because there is no implicit conversion available between JavaScript dates and Java dates – mostly arrays, strings, BigNumber, and primitives. See http://directwebremoting.org/dwr/server/dwrxml/converters for more information.

Update web.xml• WebContent/WEB-INF/web.xml

<servlet>

<servlet-name>dwr-invoker</servlet-name>

<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>

<init-param>

<param-name>classes</param-name>

<param-value>

events.Event,

events.EventManager

</param-value>

</init-param>

<init-param>

<param-name>debug</param-name>

<param-value>true</param-value>

</init-param>

</servlet>

Using DWR Debug Mode

• Go to http://localhost:8080/DWRExample/dwr/

– Click “EventManager”

– Enter “Event 1” in the first parameter of createAndStoreEvent

– Enter “10/01/2008” in the second parameter

• Click the Execute button

• Click the Execute button next to listEvents()

Let’s add DWR on the client side

• File->New->Web->HTML-> WebContent/index.html

• Open and edit title “DWR Example”

• Go back to http://localhost:8080/DWRExample/dwr/test/EventManager and copy and paste the script tags

More Dojo boilerplate• Paste the following too:

<script type="text/javascript" src="js/dojo/dojo/dojo.js"

djConfig="isDebug: true, parseOnLoad: true"></script>

<link rel="stylesheet" type="text/css"

href="js/dojo/dojo/resources/dojo.css" />

<link rel="stylesheet" type="text/css"

href="js/dojo/dijit/themes/tundra/tundra.css" />

<link rel="stylesheet" type="text/css"

href="js/dojo/dojox/grid/_grid/Grid.css" />

<link rel="stylesheet" type="text/css"

href="js/dojo/dojox/grid/_grid/tundraGrid.css" />

<script type="text/javascript">

dojo.require("dijit.form.Button");

dojo.require("dijit.form.TextBox");

dojo.require("dijit.form.DateTextBox");

dojo.require("dojox.grid.Grid");

dojo.require("dojo.data.ItemFileReadStore");

dojo.require("dojoe.dojoe");

</script>

Add Dojo.E

• Add tundra class to body tag:

<body class="tundra">

• Create XML file WebContent/dwrDojoE.xml

• And add tag

<script type="text/xml" dojoType="dojoe.XmlScript" src="dwrDojoE.xml"></script>

Edit Dojo.E

• WebContent/dwrDojoE.xml

<ui xmlns="html" xmlns:dijit="dijit" xmlns:dojox="dojox">

<dijit:form.Button>

Refresh

</dijit:form.Button>

<dojox:grid.Grid id="gridNode" jsId="grid" elasticView="0" style="height:300px; width:500px"/>

</ui>

Where’s the Grid?!?

Create a grid layout• WebContent/index.html

<body class="tundra">'

<script type="text/javascript">

var layout = [{

cells: [[

{

name: 'Event ID',

field: 'id',

width: 'auto'

},

{

name: 'Date',

field: 'dateString',

width: 'auto'

}, {

name: 'Event',

field: 'title',

width: 'auto'

}]]

}];

</script>

Note the correlation between the field values above and the RemoteProperty defintions of Event.java

Add the layout to the Grid

• WebContent/dwrDojoE.xml

<ui >

<dijit:form.Button>

Refresh

</dijit:form.Button>

<dojox:grid.Grid id="gridNode"

structure=“layout”

jsId="grid" elasticView="0" style="height:300px; width:500px"/>

</ui>

Now let’s get some data• WebContent/index.html

var putEvents = function(list){

var dataStore = new Array;

dataStore["items"] = list;

dataStore["label"] = "title";

var model = new dojox.grid.data.DojoData(null, null, {

jsId: 'model',

store: new dojo.data.ItemFileReadStore({

data: dataStore

}),

query: {

title: '*'

}

});

grid.setModel(model);

grid.refresh();

grid.render();

};

Update Dojo.E

• WebContent/dwrDojoE.xml

<ui>

<dijit:form.Button onClick="EventManager.listEvents(putEvents)">

Refresh

</dijit:form.Button>

<dojox:grid.Grid id="gridNode" jsId="grid" elasticView="0" style="height:300px; width:500px"/>

</ui>

Click Refresh

Create a method to create an event• WebContent/index.html

var createEvent = function(titleId, dateId){

var title = dojo.byId(titleId).value;

var date = dojo.byId(dateId).value;

EventManager.createAndStoreEvent(title, date);

EventManager.listEvents(putEvents);

};

Let’s Add Input Controls• WebContent/dwrDojoE.xml

<ui >

<dijit:form.Button onClick="EventManager.listEvents(putEvents)">

Refresh

</dijit:form.Button>

<dojox:grid.Grid id="gridNode" jsId="grid" structure="layout" elasticView="0“

style="height:300px; width:500px"/>

<label for="eventTitle" style="float: left;">Event Title:</label>

<dijit:form.TextBox style="float: left;" id="eventTitle" value=""/>

<label for="eventDate" style="float: left;">Event Date:</label>

<dijit:form.DateTextBox id="eventDate" value="" style="float: left;"/>

<dijit:form.Button style="float: left;“

onclick="createEvent('eventTitle', 'eventDate')" label="Add Event">

</dijit:form.Button>

</ui>

Reload the page

Enter a title and date

Load data at startup

• WebContent/index.html

dojo.addOnLoad(function() {

EventManager.listEvents(putEvents);

});

Final note

• If using more complex object models with Hibernate, use the HibernateBeanConverter3 – See http://directwebremoting.org/dwr/server/hibernate