Overview
This two part tutorial will be about using the popular data abstraction technology, Hibernate, along with Google’s “Google Web Toolkit” to build data driven web applications. It will show the basics of setting up the project, the UI, and then linking the GWT app to access a MySQL database through the Hibernate layer by building a simple employee database application.
This is the second part of our tutorial on using GWT and Hibernate. If you didn’t do part one I highly recommend doing so since I will be picking up where we left off. In this part we will be adding database functionality to our interface we previously built.
Setup the Database
First thing we need is a database to connect to. In this tutorial we will be using MySQL, but Hibernate is capable of connecting to a number of different SQL databases. Login to whatever DBMS you use (I use phpMyAdmin because I find it very simple to use) and either create a new database or navigate to whatever database you plan on using; I will be using a database named “igoedesign.” Here we will be creating a table named “employees” with the following structure:
CREATE TABLE IF NOT EXISTS `employees` (
`id` int(4) NOT NULL,
`name` varchar(255) NOT NULL,
`position` varchar(32) NOT NULL,
`department` varchar(32) NOT NULL,
`salary` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`)
)
Setting up Hibernate
To setup Hibernate we’ll actually be using a project called Gilead. You can find the zip file here. Extract it and then you will move the following files into your war/WEB-INF/lib folder.
/gilead4gwt-1.3.2.1839.jar
/gilead-core-1.3.2.1839.jar
/gilead-hibernate-1.3.2.1839.jar
/gilead-core/lib/beanlib-hibernate-5.0.2beta.jar
/gilead-core/lib/dom4j-1.6.1.jar
/gilead-core/lib/servlet-api.jar
/gilead-core/lib/slf4j-api-1.5.8.jar
/gilead-core/lib/slf4j-jdk14-1.5.8.jar
/gilead-core/lib/proxy/commons-lang-2.2.jar
/gilead-core/lib/proxy/javassist-3.4.GA.jar
/gilead-test/lib/commons-logging-1.1.jar
/gilead-test/lib/hibernate3.jar
/gilead-test/lib/hibernate-annotations.jar
/gilead-test/lib/ejb3-persistence.jar
/gilead-test/lib/runtime/antlr-2.7.6.jar
/gilead-test/lib/runtime/jta-1.1.jar
/gilead-test/lib/runtime/log4j-1.2.9.jar
Next thing we need to do is get the MySQL driver from here. Just click the “No thanks…” link and then select a mirror near you. When the download finishes extract the folder and move the mysql-connector-java-5.1.18-bin.jar into the war/WEB-INF/lib folder as well. Now that we have all of the jar files we need, we can get to configuring Hibernate to connect to our database. Right-click on the src folder in Eclipse’s package browser and choose New>File, name the file hibernate.cfg.xml. Now add this into the file making sure to change all the info to your server.
<?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 name="EmployeeDatabaseFactory">
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost/igoedesign
</property>
<property name="hibernate.connection.username">igoed</property>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.current_session_context_class">
thread
</property>
<property name="show_sql">true</property>
<!-- Mappings -->
<mapping class="com.igoedesign.blog.shared.db.Employee"/>
</session-factory>
</hibernate-configuration>
In this you’ll see a few interesting things, the first thing that jumps out immediately is all of the access information to connect to our database, make sure you change these things to reflect your setup. Next thing is the dialect property, Hibernate uses this to determine how our queries are generated into true SQL. The current_session_context_class property defines how Hibernate will manage the user’s session, by setting it to thread we are using Hibernate’s built-in session manager.
First, in addition to implementing the IsSerializable interface make it extend the LightEntity class. These two things allow GWT to serialize the object and send it over the wire to be used. Next, we need to add two annotations to the Employee class, @Entity and @Table(name=”employees”). These let the application know what that this class is representing a database table and exactly which table it is representing. To determine which values are mapped to a column in the table we use @Column(name=”
“), you can put this annotation either just before the declaration of the field or just before the getters and setters for that variable (I prefer putting it before the getters and setters). Following these the Employee class should look like this:
@Entity
@Table(name = "employees")
public class Employee extends LightEntity implements IsSerializable {
private int id;
private String name;
private String position;
private String department;
private double salary;
@SuppressWarnings("unused")
private Employee() {}
public Employee(int id, String name, String position,
String department, double salary) {
this.id = id;
this.name = name;
this.position = position;
this.department = department;
this.salary = salary;
}
@Id
@Column(name="id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name="position")
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
@Column(name="department")
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
@Column(name="salary")
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
You’ll notice I have a private empty constructor, the reason for this is that in order for something to be serialized by GWT it needs either no constructors or if you define one (like we are using the explicit one) you need to have an empty constructor with any visibility. You can read more about GWT’s serialization rules here. Also, every POJO which is being mapped to a table in a database needs to have an Id. If your table has a composite key you can define one using an @EmbeddedId. For example, say you wanted to create a key which was a composition of employee id and some project id.
@Embeddable
@LightEntity
public class EmployeeProjectPK extends LightEntity implements IsSerializable {
@Column(name="employees.id")
public int employeeId;
@Column(name="projects.id")
public int projectId;
}
// And then you'd use these lines in your mapping class to declare it
@EmbeddedId
public EmployeeProjectPK primaryKey;
Now let’s get to the model of our Model-View-Controller. First, in the client package you’re going to need to make two interfaces for our remote service. We can name them EmployeeService (this should extend the RemoteService interface) and EmployeeServiceAsync, in GWT every remote service needs to have an asynchronous counter-part since GWT applications use AJAX-style RPCcalls. For now we can just have them be empty and we’ll come back to them when we’re implementing some functionality.
Our service implementation will go in the server package and be named EmployeeServiceImpl, it should implement our EmployeeService and extend RemoteServiceServlet. We need to initialize a few things in the constructor so right now your service implementation should look like this:
public class EmployeeServiceImpl extends RemoteServiceServlet implements EmployeeService {
private HibernateUtil util;
public EmployeeServiceImpl() {
SessionFactory sf = AnnotationConfiguration.configure().buildSessionFactory();
util = HibernateUtil.getInstance();
util.setSessionFactory(sf);
}
}
The session factory object we create here looks for the hibernate.cfg.xml file we created before; you can change it to look for a different one, but it is much more convenient to just use this. We will use the HibernateUtil singleton in order to access our session and begin/commit transactions. Now we need to tell our application where it can find our servlet, navigate to the war/WEB-INF/web.xml file in your project. Here you’ll see pre-generated code pertaining to the GreetingService that is used as sample code, you can get rid of all of that and add this:
<servlet>
<servlet-name>empServlet</servlet-name>
<servlet-class>com.igoedesign.blog.server.EmployeeServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>tutServlet</servlet-name>
<url-pattern>/gwthibernatetut/emp</url-pattern>
</servlet-mapping>
You’re also going to have to add @RemoteServiceRelativePath(“emp”) to the EmployeeService interface so that it looks like this:
@RemoteServiceRelativePath("emp")
public interface EmployeeService extends RemoteService {
}
We’re almost able to start adding functionality to our interface, but first we need to make a controller to allow communication between the model and the view. Make an EmployeeController class in the controllers package we made in the last part that has an EmployeeView and an EmployeeServiceAsync as fields. It’s constructor should look like this (assuming your EmployeeView and EmployeeServiceAsync fields are named panel and server, respectively):
public EmployeeController(EmployeeView panel) {
this.panel = panel;
server = GWT.create(EmployeeService.class);
}
The GWT.create() method takes a class literal and returns an instance of it, by instantiating our asynchronous service like this GWT is able to make RPC calls to our servlet without interrupting the user’s experience on the site. Now we need to make an EmployeeController field in the EmployeeView class and instantiate it in the constructor by passing “this” into the constructor.
Connecting the Interface and Database
Now we’re ready to start linking our interface up to the database. The first thing we’ll do is add functionality to our “Add Employee” button. You’ll need to add the following declarations to the EmployeeService and EmployeeServiceAsync interfaces.
// This goes into EmployeeService
public void addEmployee(Employee emp);
// This goes into EmployeeServiceAsync
public void addEmployee(Employee emp, AsyncCallback<Void> callback);
For every method in the remote service we need to have a corresponding one in the asynchronous class. Every method in the asynchronous class will have a void return type and then the return type of the remote service as the AsyncCallback’s type (since null can be returned you must use wrapper classes for primitives just as you do for ArrayList’s).
Next, let’s add the definition of this to our servlet. Inserting new rows to our database with Hibernate is incredibly easy. Since Employee is already mapped to a table, all we need to do is retrieve our current session with “util.getSessionFactory().getCurrentSession()”, use this session object to begin a new transaction, and then pass the Employee object to the session’s save method and commit. Our addEmployee method in EmployeeServiceImpl should look like this:
@Override
public void addEmployee(Employee emp) {
Session session = util.getSessionFactory().getCurrentSession();
session.beginTransaction();
session.save(emp);
session.getTransaction().commit();
}
That’s it! This is all we need to add a new employee to our database. So lets make an addEmployee method to our controller like this:
public void addEmployees(Employee emp) {
server.addEmployee(emp, new AsyncCallback() {
@Override
public void onFailure(Throwable caught) {
caught.printStackTrace();
}
@Override
public void onSuccess(Void result) {
// You could display some sort of message here
}
});
}
And now go into the click handler of our submit button in EmployeeView and make a call to this with the EmployeeController field object we created earlier. There we are, now we’re able to add new employees to our database, but that doesn’t mean much if it doesn’t get displayed when we load the page and also if you remember we were determining the next employee id programmatically so we will also run into some primary key problems right now.
In order to display the employees currently in the database when the page loads, we’re going to need to add a few things. First, we need our server implementation which will use Hibernate to gather all of the data. So using the add employee method as a template, go ahead and add the definition in both the remote interface and the async interface (I can’t just give you all the code); let’s call the method getEmployees and it will have no parameters and List as the return type. Next thing we need to add is the server implementation. This code I will give you since there’s a few things that are confusing and I do some sorting to make sure its easy to find the new ID when the page loads up.
@SuppressWarnings("unchecked")
@Override
public List getEmployees() {
Session = util.getSessionFactory().getCurrentSession();
session.beginTransaction();
Query q = session.createQuery("from Employee");
List ret = q.list();
session.getTransaction().commit();
if (ret.size() > 0) {
Collections.sort(ret, new Comparator() {
@Override
public int compare(Employee arg0, Employee arg1) {
return (arg0.getId() - arg1.getId());
}
});
return ret;
}
return null;
}
As you can see we start this method off just like addEmployee, but after we create the session object we do something a little different. Now we create a Query object from our session. The string you pass into createQuery is your query written in HQL (you can find more documentation about HQL on Hibernate’s site). Just typing “from Employee” is the equivalent of writing “SELECT * FROM employee” in SQL (in fact you can actually write this out and have it work, but let’s be serious). Then we pass the list into the Collections.sort method with a Comparator which just compares the two employee’s ID’s.
Now we’ll make a corresponding getEmployees method in the EmployeeController class, use the addEmployees method as a guide, but just put this line in the onSuccess method of the callback:
panel.displayEmployees(result);
As you can see we don’t have this method defined yet, but we are going to do that in just a second. So switch over to the EmployeeView class, we want the getEmployee method to be called every time the page loads. The best place for that is in the constructor so add a call to the controller’s getEmployee method just after the call to initColumns. Now for the displayEmployees method:
public void displayEmployees(List emps) {
if (emps != null) {
nextId = emps.get( emps.size()-1 ).getId() + 1;
dataProvider.setList(emps);
}
}
The first thing we need to do is make sure that the server call didn’t return a null value, which would mean that our database holds no employee information at that time. Next, we get the last employee in the list and set nextId to one higher that its ID (since we sorted the list on the server side this will guarantee that it will always be the nextId. Then we use the data provider’s built-in setList method associate the list to our grid.
There you have it, you can now add employees and have your work persist until your next visit. As an exercise I’ll leave removing employees up to you, but I will give you one tip, since a user can use SHIFT and CTRL to multiselect employees you’re probably going to want to pass a Set of employees to your method and not just a single Employee.
Conclusion
In these two parts we have learned how to use some very powerful tools. With GWT and Hibernate you can quickly make some very powerful user interfaces and get them hooked up to a database very easily. The ways I showed you here are just basic ways that I figured out when I was first learning, there are many ways to set everything up.
If you have any questions about working with GWT and Hibernate, drop them in the comments section or send us an e-mail.