Hibernate + Spring + ZK
Fernando De Leon, Potix Corporation, Senior Software Web Developer, Queensland Treasury Corporation
March 24, 2006
Applicable to ZK 2.1.1 and later.
- Applicable to ZK 1.2.0 and later.
Overview
ZK is a very flexible and powerful Ajax framework. As the former suggest it belongs to the presentation tier of a web application. Keeping a level of abstraction between the presentation and data layers is paramount to maintaining a scalable web application.
Hibernate is a popular Object Relational Model (ORM) framework and Spring is an all rounder framework, but one of its key uses is for managing Hibernate resources in relation to connectivity to a database.
Note this is not THE way of integrating ZK with Hibernate and Spring there are many ways but for presentation purposes I am going to show a particular common way of integrating.
Step by Step
For this presentation I am going to make a small application that connects to a MySQL database which has only one table.
Step 1: MySQL Database Schema
Create a database in MySQL called FRIEND_DB with table name PERSON.
Field | Type |
---|---|
id | Int |
name | Varchar(120) |
surname | Varchar(120) |
Step 2: Setup Hibernate with Spring
Here we assume we are using Hibernate 3. Put all the required Hibernate 3 libraries in to your WEB-INF/lib directory. Along with all the Spring libraries in your WEB-INF/lib directory. (Consult Hibernate and Spring manuals for appropriate libraries to use).
At the root of the source of the project we create a Spring configuration file, this file defines the data sources, session factory and DAOs that are needed to manage Hibernate resources and manage the business objects.
<?xml version="1.0" encoding="UTF-8"?>
<!-- ApplicationContext.xml -->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://127.0.0.1:3306/friend_db</value>
</property>
<property name="username">
<value>dba</value>
</property>
<property name="password">
<value>dba</value>
</property>
</bean>
<bean id="factory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>org/zk101/model/pojo/Person.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">
false
</prop>
<prop key="hibernate.transaction.factory_class">
org.hibernate.transaction.JDBCTransactionFactory
</prop>
</props>
</property>
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="personDao" class="org.zk101.dbaccess.dao.PersonDAO">
<property name="sessionFactory">
<ref bean="factory" />
</property>
</bean>
<bean id="personManager" class="org.zk101.service.PersonManagerImpl">
<property name="dao">
<ref bean="personDao"/>
</property>
</bean>
</beans>
The configuration file above manages Hibernate’s connection to MySQL database. It also manages the injection of DAOs which are needed to perform different operations on the table (e.g., CRUD operations).
Notice also that spring is also able to manage the business layer, review the person manager declaration. The person manager is a business object that uses a person DAO for to define different business rules.
For completeness these are the following Hibernate resources. A XML file, Person.hbm.xml, describes the Person bean.
<?xml version="1.0"?>
<!-- Person.hbm.xml -->
<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping
package="org.zk101.model.pojo">
<class name="Person" table="person" lazy="false">
<id name="id" type="long">
<generator class="increment"/>
</id>
<property name="name" />
<property name="surname"/>
</class>
</hibernate-mapping>
Also, a Java file, Person.java, to represent the Person bean in memory.
/** Person.java */
package org.zk101.model.pojo;
public class Person {
private Long id = new Long(-1);
private String name;
private String surname;
//ommiting getters and setters
}
The purpose of Spring doesn’t stop just by providing the service for injecting DAOs. In fact Spring provides with a DAO support object which is a base DAO that your DAOs can use. This Spring base DAO enables you to perform the simple CRUD operations in a simple manner.
/** PersonDAO.java */
package org.zk101.dbaccess.dao.PersonDAO;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class PersonDAO extends HibernateDaoSupport {
public void saveOrUpdate(Object ob) {
super.getHibernateTemplate().saveOrUpdate(ob);
}
public void delete(Object ob) {
super.getHibernateTemplate().delete(ob);
}
public Object find(Class clazz, Long id) {
Object ob = super.getHibernateTemplate().load(clazz,id);
return ob;
}
public List findAll(Class clazz) {
List list = super.getHibernateTemplate().find("from"+clazz.getName());
return list;
}
}
The PersonDAO inherits HibernateDaoSupport which is a DAO support object provided by Spring. It is this HibernateDaoSupport that has a setter called setSessionFactory (SessionFactory sfactory) And this is how you are abstracted from the complexity of opening and closing a Hibernate session. Please review Spring manual in utilizing other more complex non CRUD operations utilizing an inner method called doInHibernate (Session session).
Now that we have a DAO which we can do simple CRUD operations on our Person table we need the business object (PersonManager) that will support our business rules. Note Spring manages the injection of the PersonManager object also.
In keeping with well define OOP principles we define a person manager interface.
/** PersonManager Interface */
package org.zk101.service;
import org.zk101.model.pojo.Person;
public interface PersonManager {
public List getAllPersons();
/** for simplicity we are only providing a simple business rule method **/
}
And, the implemenation:
/** PersonManagerImpl.java */
package org.zk101.service;
import org.zk101.model.pojo.Person;
import java.util.*;
public class PersonManagerImpl implements PersonManager {
private PersonDAO dao;
public PersonDAO getDao() {
return this.dao;
}
public void setDao(PersonDAO personDAO) {
this.dao = personDAO;
}
public List getAllPersons() {
return this.dao.findAll(Person.class);
}
}
Inspect the ApplicationContext xml notice how we are injecting the personManager and the reason the operations work in getAllPersons() is because the applicationContex injects the personDAO bean into a setDao() method.
All of this is good enough, however so far we had described the Hibernate + Spring configuration. But whatever happened to ZK?
ZK should be agnostic to the data layer and it should not know about business rules, it should only use them.
We have seen how Spring’s ApplicationContext.xml file is the central hub for managing the data and business layer resources. However how is the view layer able to communicate to the business layer? For this we use a special object that ZK can call to load up a manager. This becomes the link between the presentation layer and the business layer. We use a ServiceLocator object. ServiceLocator is responsible in loading up the applicationContext.xml file and thus inspect this file and provide a service to locate different managers. Therefore ServiceLocator would use many managers (In our case it will simply use one manager).
/** ServiceLocator.java */
package org.zk101.service;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.zk101.service.PersonManager;
public class ServiceLocator {
private static ApplicationContext ctx;
static {
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
private ServiceLocator() {
}
public static SessionFactory getSessionFactory() {
return (SessionFactory) ctx.getBean("factory",SessionFactory.class);
}
public static PersonManager getPersonManager() {
return (PersonManager) ctx.getBean("personManager",PersonManager.class);
}
}
The service locator is created as a singleton object, as the loading up of the applicationContext.xml needs to happen only once!
It is therefore your ZK object that needs to use the ServiceLocator to get a personManager object to perform the desire business operations.
Step 3: Implementing User Interfaces with ZK
Our ZK application will be a simple screen that will display a list of all persons.
We will first develop the person.zul, this file will contain our listbox which will display all the persons found in our database.
Notice how we populate our list box. One of the strengths of ZK is that it facilitates for a very clean codeless view page. By codeless it means that you no longer need to see scriptles (java codes) in our view page. This is the strategy we are going to use.
<!-- person.zul -->
<vbox>
<listbox id="personList" width="800px" rows="5"
use="org.zk101.ui.ListPerson">
<listhead>
<listheader label="Name"/>
<listheader label="Surname"/>
</listhead>
</listbox>
</vbox>
Notice how this zul file (person.zul) doesn’t have any java codes or scriplets embedded? This allows for a very clean view page. We in fact are extending Listbox, it is inside ListPerson.java that we make use of our PersonManager.
/** ListPerson.java */
package org.zk101.ui;
import org.zk101.service.ServiceLocator;
import java.util.*;
import org.zk101.service.PersonManager;
import org.zk101.model.pojo.Person;
import org.zkoss.zul.Label;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
public class ListPerson extends Listbox {
public void onCreate() {
PersonManager manager = ServiceLocator.getPersonManager();
Collection allPersons = manager.getAllPersons();
Iterator it = allPersons.iterator();
while(it.hasNext()) {
Person person = (Person) it.next();
Long id = person.getId();
String name = person.getName();
String surname = person.getSurname();
Listitem listitem = new Listitem();
listitem.setValue(id);
listitem.setParent(this);
Listcell nameCell = new Listcell(name);
nameCell.setParent(listitem);
Listcell surnameCell = new Listcell(surname);
surnameCell.setParent(listitem);
}
}
}
As you can see ListPerson.java which is a class that inherits from Listbox is use to populate our zul list box.
ZK doesn’t need to know about whether we use hibernate, the PersonManager abstracts that information, and from the perspective of ZK coder, the PersonManager is a service object.
Similarly Hibernate doesn’t need to know what presentation framework you are using. This pattern allows for a extensible and scalable system, as well as highly maintainable.
Alternative Implementation with ZK: Initiator
By use of org.zkoss.zk.ui.util.Initiator
, we could separate the code and the creation of XUL components further.
First, implementing Initiator to provide a collection of all persons.
/** AllPersons.java */
package org.zk101.ui;
import org.zk101.service.ServiceLocator;
import org.zk101.service.PersonManager;
import org.zk101.model.pojo.Person;
import org.zkoss.zk.ui.util.Initiator;
public class AllPersons implements Initiator {
public void doInit(Page page, Object[] args) {
PersonManager manager = ServiceLocator.getPersonManager();
page.setVariable("allPersons", manager.getAllPersons());
}
public void doCatch(Throwable ex) {
}
public void doFinally() {
}
};
Then, we can implement the view page as follows. Notice that we use the setVariable method to communicate with a ZUML page.
<?init class="org.zk101.ui.AllPersons"?>
<!-- person2.zul -->
<vbox>
<listbox id="personList" width="800px" rows="5">
<listhead>
<listheader label="Name"/>
<listheader label="Surname"/>
</listhead>
<listitem value="${each.id}" forEach="${allPersons}">
<listcell label="${each.name}"/>
<listcell label="${each.surname}"/>
</listitem>
</listbox>
</vbox>
Alternative Implementation with ZK: zscript
For fast prototyping or developers who don't mind to mix some codes with the view page, we can use the zscript element to do all UI implementation all in the view page.
<!-- person3.zul -->
<vbox>
<zscript;><![CDATA[
allPersons = ServiceLocator.getPersonManager().getAllPersons();
]]></zscript;>
<listbox id="personList" width="800px" rows="5">
<listhead>
<listheader label="Name"/>
<listheader label="Surname"/>
</listhead>
<listitem value="${each.id}" forEach="${allPersons}">
<listcell label="${each.name}"/>
<listcell label="${each.surname}"/>
</listitem>
</listbox>
</vbox>
Summary
This article illustrates how to integrate Hibernate and Spring with ZK.
I don’t intend to be an expert and I encourage users to redefine the pattern concepts.
Update History
1. April 3, 2006: Initiator is moved to com.potix.zk.ui.util since 1.2.0.
2. September 23, 2006: org.zkoss is used since 2.1.0.
Fernando De Leon is a Senior Software Web Developer (6yrs experience) working for Queensland Treasury Corporation (Brisbane, Australia). He basically focuses on web development and design with a key interest in AJAX frameworks as well as having experience at the database tier. The main ORM experience that he has is with Hibernate 3.
Copyright © Fernando De Leon. This article is licensed under GNU Free Documentation License. |