Struts Recipes: Hibernate and Struts

Add the power of Hibernate to your Struts application

Persistence is a fundamental piece of an application. Obviously, without persistence all work would be lost. However, persistence means different things to different people. The length of time something must be persisted is a fundamental qualifier in choosing a persistence storage medium. For example, the HTTP session may be suitable when the life of a piece of data is limited to the user's session. In contrast, persistence over several sessions, or several users, requires a database. The volume of data is another important qualifier. For example, best practices suggest large amounts of data should not be stored in an HTTP session. In those circumstances, you need to consider a database. In this recipe we target persistence in a database.

The type of database you choose has an important influence on your architecture and design. As object-oriented developers, we tend to represent data as an interconnected web of objects as a means to describe the business problem at hand—this is often called a domain model. However, the most common storage medium is based on a relational paradigm. Unless our object model mirrors a relational structure, the in-memory representation of our data is at odds with the means to persist it. This problem is called the mismatch paradigm. One of the most popular tools to address the mismatch problem is a category of tools called object-relational mappers. An object-relational mapper is software used to transform an object view of the data into a relational one, and provide persistence services, such as create, read, update, and delete (CRUD). Many good papers have been written on object-relational mappers, but in essence, they all speak to the Data Mapper pattern. One of the most popular object-relational mappers is the open source Hibernate project.

In this recipe, we show you how to employ Hibernate in a Struts application. In addition, we will show you how to create a Struts plug-in to give your Hibernate-powered Struts applications a performance boost.

Recipe

In this recipe, we use an example to illustrate everything you need to do to use Hibernate in a Struts application. We create an application to retrieve and display elements from the chemical periodic table. The application offers the user a search page to look for an element by element symbol. The application responds by searching the database for an element matching the symbol name and returns information about the element.

We'll start by showing you how to get the Hypersonic database server up and running. With the database server started, we create the table and data required to exercise the application. Once the database is ready to go, we'll create all the Hibernate artifacts required to execute this application by using the Hypersonic database server. The next step is to respond to search requests by calling upon Hibernate to handle database access from inside our Action. Because creating Hibernate factory objects is expensive, we'll create a Struts plug-in to create the factory and store it in context.

Let's start by bringing up the Hypersonic database server. You need to download Hypersonic from http://hsqldb.sourceforge.net/. Place hsqldb.jar in your classpath and launch Hypersonic by entering the following command in your DOS prompt:

 java org.hsqldb.Server

Although the server's response varies from one version of Hypersonic to another, the following response is a typical indication that Hypersonic is ready to serve database requests.

 Server 1.6 is running
Press [Ctrl]+{c} to abort 

With the database server up and running, we are ready to create the elements table and populate it with data, as shown in Listing 1.

Listing 1. Create and populate elements tables

 

create table elements (id integer(3) IDENTITY, name char(30), number char(30), mass char(30), symbol char(2));

CREATE UNIQUE INDEX ui_elements_pk ON elements (symbol)

insert into elements ( name, number, mass, symbol) values ('Manganese','25','55','Mn'); insert into elements ( name, number, mass, symbol) values ('Zinc','30','65','Zn'); insert into elements ( name, number, mass, symbol) values ('Thulium','69','169','Tm'); insert into elements ( name, number, mass, symbol) values ('Californium','98','251','Cf'); insert into elements ( name, number, mass, symbol) values ('Gold','79','197','Au'); insert into elements ( name, number, mass, symbol) values ('Ytterbium','70','173','Yb'); insert into elements ( name, number, mass, symbol) values ('Molybdenum','42','96','Mo'); insert into elements ( name, number, mass, symbol) values ('Palladium','46','106','Pd');

Listing 1 presents the SQL commands necessary to create the elements table, create a unique index on symbol, and insert data We have only presented a few of the periodic elements. We'll leave it to you to dust off your high school chemistry textbook to create data for the remaining elements.

Listing 2 presents the Element JavaBean used to store data retrieved from the database.

Listing 2. Element JavaBean

 

package com.strutsrecipes.hibernate.beans;

public class Element { private String name; private String symbol; private String number; private String mass; private int id;

public Element() { super(); }

public Element(String name, String symbol, String number, String mass) { this.name = name; this.symbol = symbol; this.number = number; this.mass = mass;

}

public int getId() { return id; }

public void setId(int id) { this.id = id; }

public String getMass() { return mass; }

public String getName() { return name; }

public String getNumber() { return number; }

public String getSymbol() { return symbol; }

public void setMass(String mass) { this.mass = mass;

}

public void setName(String name) { this.name = name; }

public void setNumber(String number) { this.number = number; }

public void setSymbol(String symbol) { this.symbol = symbol; } }

Hibernate is an object-relational mapping tool. Its job is to map objects to relational tables and vice versa. Therefore, we must tell Hibernate how to map the columns in the "elements" table to the properties of the Elements JavaBean. This is done using the Element.hbm.xml file. The information embodied in this file is required to empower Hibernate to copy data from the table to the Elements JavaBean. If we were using Hibernate to update data, the information in the Element.hbm.xml file would be used to extract data from the Elements JavaBean to generate SQL update statements. Listing 3 presents Element.hbm.xml.

Listing 3. Element.hbm.xml

 

<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sf.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping> <class name="com.strutsrecipes.hibernate.beans.Element" table="elements">

<id name="id" column="id"> <generator class="native"/> </id>

<property name="name" column="name"/> <property name="number" column="number"/> <property name="mass" column="mass"/> <property name="symbol" column="symbol"/> </class> </hibernate-mapping>

Let's step through Listing 3

We declare the full package name of the class to be associated with the "elements" table. We then declare the name of the table associated with that class. Next, we declare the mapping from the id JavaBean property to the id column. Because the property and column names have the same value, we could have omitted the column attribute, but we have explicitly declared the column for clarity purposes. The <id> tag is a special tag. It is used to declare the primary key for the table. The enclosing <generator> tag instructs Hibernate to generate the key in whichever way is most appropriate for the database implementation. You should consult Hibernate documentation for more information on the <id> tag. Finally, we declare mapping for the remaining JavaBean properties. Once again the column attribute is declared for clarification purposes.

Once the mapping file has been broken down in detail, it's all rather straightforward. It simply describes which table maps to which class and which JavaBean properties map to which column names. Later on we will tell you where to place this file.

Next, we configure Hibernate by declaring environmental information. In Listing 4, we present the hibernate.cfg.xml file.

Listing 4. hibernate.cfg.xml

 

<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration> <session-factory> <property name="dialect">net.sf.hibernate.dialect.HSQLDialect</property> <property name="connection.driver_class">org.hsqldb.jdbcDriver</property> <property name="connection.username">sa</property> <property name="connection.password"></property> <property name="connection.url">jdbc:hsqldb:hsql://127.0.0.1</property> <property name="show_sql"> </property> <property name="">true</property>

<mapping resource="/com/strutscookbook/hibernate/beans/Element.hbm.xml"/> </session-factory> </hibernate-configuration>

Let's step through Listing 4.

We start by specifying the database implementation dialect that allows Hibernate to take advantage of implementation-specific features. We declare the Hypersonic dialect. You should consult the Hibernate documentation to choose the appropriate dialect for your database. We then declare the database driver. You must ensure this driver is in your application's classpath. We then declare the database username, the database password, and the database connection URL. Next, we instruct Hibernate to display the SQL generated at runtime in the log.

The hibernate.cfg.xml file must be placed in your classpath.

The procedure to use Hibernate within your application requires the following steps:

  1. Create a Hibernate configuration object
  2. Use the Hibernate configuration object to create a Hibernate factory object
  3. Use the Hibernate factory object to create a Hibernate session object
  4. Use the Hibernate session object to start a transaction (optional)
  5. Employ the Hibernate session object to create, read, update, and delete data on the database
  6. Commit the transaction (optional)
  7. Close the session

A Hibernate best practice is to create and cache the Hibernate factory to enhance performance. Therefore, we will create a Struts plug-in to perform Steps 1 and 2 and cache the Hibernate factory in the servlet context, as shown in Listing 5.

Listing 5. HibernatePlugin.java

 

package com.strutsrecipes.hibernate.plugin;

import java.net.URL; import javax.servlet.ServletException;

import net.sf.hibernate.HibernateException; import net.sf.hibernate.MappingException; import net.sf.hibernate.SessionFactory; import net.sf.hibernate.cfg.Configuration;

import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.action.ActionServlet; import org.apache.struts.action.PlugIn; import org.apache.struts.config.ModuleConfig;

public class HibernatePlugin implements PlugIn { private Configuration config; private SessionFactory factory; private String path = "/hibernate.cfg.xml"; private static Class clazz = HibernatePlugin.class;

public static final String KEY_NAME = clazz.getName();

private static Log log = LogFactory.getLog(clazz);

public void setPath(String path) { this.path = path; }

public void init(ActionServlet servlet, ModuleConfig modConfig) throws ServletException {

try { URL url = HibernatePlugin.class.getResource(path); config = new Configuration().configure(url); factory = config.buildSessionFactory(); servlet.getServletContext().setAttribute(KEY_NAME, factory);

} catch (MappingException e) { log.error("mapping error", e); throw new ServletException();

} catch (HibernateException e) { log.error("hibernate error", e); throw new ServletException(); }

}

public void destroy() { try { factory.close(); } catch (HibernateException e) { log.error("unable to close factory", e); } } }

Creating a Struts plug-in requires only two steps. First, create a class implementing org.apache.struts.action.PlugIn (Listing 5). Second, define a <plug-in> tag in the struts-config.xml file (Listing 6).

Let's step through Listing 5.

1 2 Page 1
Page 1 of 2