Using Hibernate to Persist Your Java Objects to IBM DB2 Universal Database

Hibernate, an open source application, provides a way to easily persist your Java objects to DB2 Universal Database without writing a single line of SQL code. Our authors show you how.

Share:

Javid Jamae, Independent software consultant , Jamae Consulting

Javid Jamae is an independent software consultant who specializes in enterprise application and software methodology consulting. You can contact Javid at javidjamae at yahoo.com.



Kulvir Bhogal, WebSphere consultant, IBM

Photo: Kulvir Singh BhogalKulvir Singh Bhogal works as a WebSphere consultant, implementing IBM's e-business strategies across the United States. You can contact Kulvir at kbhogal at us.ibm.com.



19 June 2003

Introduction

IBM DB2 e-kit for Database Professionals

Learn how easy it is to get trained and certified for DB2 for Linux, UNIX, and Windows with the IBM DB2 e-kit for Database Professionals. Register now, and expand your skills portfolio, or extend your DBMS vendor support to include DB2.

Let's face it, if you're hand-coding SQL statements in your enterprise application, you are spending a significant amount of your development time updating and maintaining your persistence layer. Wouldn't it be nice if you could easily persist your existing JavaTM objects to a relational database like IBM® DB2® Universal DatabaseTM (UDB)?

Fortunately, there is way to do this. Object/Relational (O/R) mapping tools are sophisticated tools that map objects to rows in a relational database, obviating the need for a complicated persistence layer and minimizing, and in most cases eliminating, the need for developers to write any SQL.

Hibernate, an open source application released under the LGPL license, is an "ultra-high performance object/relational persistence and query service for Java." In this article, we'll show you how you can use Hibernate to easily persist a Java object to a DB2 database without writing a single line of SQL code.

In order to demonstrate how Hibernate works, we will create a simple class model, consisting of two classes: Employee and Department. For the sake of simplicity, an employee will have a department, but a department will have no references to an employee. See Figure 1 for the class diagram.

Figure 1. Employee/Department class diagram
Employee/Department class diagram

We will develop our application using the Application Developer configuration of WebSphere® Studio 5.0 along with a plugin called Hibernator, which simplifies some of the work involved in configuring Hibernate.


Setting up WebSphere Studio and the Java project

First, let's take some time out to prepare the ingredients for our experiment:

  • Create a new Java Project in WebSphere Studio.
  • Download Hibernate from http://sourceforge.net/projects/hibernate/. At the time we wrote this article, Hibernate was in its 1.2.4 incarnation.
  • Unzip the Hibernate archive you grabbed from SourceForge, and extract the contents to a temporary directory.
  • Import the JAR file named hibernate.jar into the base directory of your project.
  • Import the following jar files from the lib directory in the Hibernate distribution:
  • Add hibernate.jar to your Java Build Path (right click on your project -> Properties -> Java Build Path -> Libraries -> Add JARs... and point to the hibernate.jar file you imported).
  • Download the Hibernator Eclipse plugin from SourceForge (http://sourceforge.net/projects/hibernator/). As you'll see, the plugin makes it easier to synchronize an existing Java class and a Hibernate mapping file that defines the rules for our O-R mapping. At the time we wrote this article, the version of the plugin was 0.9.3.
  • Unzip the plugin zip file to the [WSAD 5 InstallDir]\eclipse\plugins\ directory.
  • To interact with DB2 UDB, we also need to import our DB2 JDBC database driver. Import the db2java.zip file located by default in your C:\program files\IBM\SQLLIB\java\ directory. Make sure to add db2java.zip to your classpath.
  • We've included some JUnit tests in the attached code for this article. If you would like to run these tests, you will need to import the junit.jar file located in the [WSAD5InstallDir]\eclipse\plugins\org.junit_3.7.0 directory by default.
  • We must restart WebSphere Studio in order for it to register the plugin that we added.

Configuring hibernate.properties

To facilitate communication with DB2 UDB, we need to let Hibernate know some of our database properties. We'll do this by creating a file called hibernate.properties, which must appear in our application's classpath. In our example, we will put the properties file in our project's base directory, which is included in the classpath. You may need to change property values below for your own database setup.

hibernate.connection.driver_class = COM.ibm.db2.jdbc.app.DB2Driver 
hibernate.connection.url = jdbc:db2:empl 
hibernate.connection.username = db2admin 
hibernate.connection.password = db2admin 
hibernate.dialect = cirrus.hibernate.sql.DB2Dialect

If you've ever had to write code to retrieve a JDBC connection, then the first four parameters should be familiar to you. The hibernate.dialect property tells hibernate that we are using a DB2 "dialect." Setting up this dialect allows Hibernate to enable several DB2-specific features by default, so that you don't have to set them up manually.


Create the database schema

The property file above refers to a database named empl, which we have not yet created. Let's go ahead and do this. Let's go to the DB2 Command Line Processor:

db2=> create db empl 
 
db2=> connect to empl user db2admin using db2admin

Also, we need some tables to play with:

db2=> create table Employee ( 
	EID int NOT NULL PRIMARY KEY, 
	FirstName varchar(30) NOT NULL, 
	LastName varchar(30) NOT NULL, 
	Email varchar(30) NOT NULL, 
	ManagerEID int, DepartmentID int NOT NULL) 
 
 
db2=> create table Department( 
DepartmentID int NOT NULL PRIMARY KEY, 
	Name varchar(30) NOT NULL, 
	City varchar(30) NOT NULL, 
	State varchar(30) NOT NULL)

Create the JavaBeans to be mapped

If you recall, the motivation behind you reading this article is to map our Java objects to the database. So let's define those objects:

Create new class Department:

package com.ibm.hibernate_article; 
 
public class Department 
{ 
	private int departmentID; 
	private String name; 
	private String city; 
	private String state;}

Create new class Employee:

package com.ibm.hibernate_article; 
 
public class Employee 
{ 
 
	private int employeeId; 
	private String firstName; 
	private String lastName; 
	private String email; 
	private Employee manager; 
	private Department department;}

For our newly created classes:

  1. Right-click on the class name in the outline view.
  2. Select Generate Getter and Setter...
  3. Select All.
  4. Click OK.

Keep in mind that all of your setters and getters must exist, but their visibility does not matter. Thus, if you wanted to maintain immutable objects, you could set your object state during construction and make all the setter methods private. You would have to provide a default constructor in addition to any other constructors you created; but, the default constructor can have private visibility as well. The reason the setter and getter methods and the default constructor must exist is because Hibernate abides by the JavaBean syntax and uses these method signatures in order to persist data during O/R mapping.


Create the XML mapping

Now that we have our Java classes and database tables in place, we need to define the O/R mapping. Hibernate reads XML files that contain mapping definitions in order to achieve this goal.

Let's create a mapping for our Employee class first.

  1. Open the Employee.java file in the editor.
  2. Click on Window -> Show View -> Other -> Hibernator -> Hibernator (see Figure 2).

    Figure 2. Showing the Hibernator view


    Showing the Hibernator view
  3. Right-click in the Hibernator view and click Save (Figure 3).

    Figure 3. Using the Hibernator plugin to generate the O/R mapping XML file


    Using the Hibernator plugin to generate the O/R mapping XML file

We still have to do a little bit of editing, but this view does not really provide editing functionality, it just generates a .hbm.xml file. Thus, we will need to open the Employee.hbm.xml file in the regular file editor.

Analyzing the mapping file

The plugin has generated a file that should look like this:

<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC 
  "-//Hibernate/Hibernate Mapping DTD//EN" 
  "http://hibernate.sourceforge.net/hibernate-mapping.dtd"> 
 
<hibernate-mapping> 
  <class name="com.ibm.hibernate_article.Employee" table="employee"> 
    <many-to-one name="department"/> 
    <property name="email"/> 
    <property name="employeeId"/> 
    <property name="firstName"/> 
    <property name="lastName"/> 
    <many-to-one name="manager"/> 
  </class> 
 
</hibernate-mapping> 
<!-- parsed in 0ms -->

You will notice that the document type definition (DTD) file defined in the DOCTYPE exists at the specified URL (i.e., http://hibernate.sourceforge.net/hibernate-mapping.dtd ). If the mapping file exists in the classpath, Hibernate will always reference it from their first. Because it is in the hibernate.jar file, it will be in your classpath, and thus you shouldn't have to worry about importing it manually. This file is used to define the valid tags allowed in the XML file.

The <hibernate-mapping> tag is the base tag in the XML file. This tag has two optional attributes, but we will not need these for our application. See the Hibernate documentation to learn more about these features.

The <class> element represents a persistent Java class. It has a name attribute that refers to the fully qualified (dot delimited) class name of the Java class that we're mapping. It also has a table attribute that refers to the database table that our class maps to (that is, the employee table). The plugin did not generate the table attribute for us, so we will add it in the following section.

The <class> element must also contain an <id> element in order to specify which field acts as the primary key for the database table, and to specify how the primary key is generated. We will discuss this in the next section as well.

The Hibernate documentation says that "the <property> element declares a persistent, JavaBean style property of the class." This is mainly used for instance variables that are primitives or Strings. In our example, the employee's first name would be represented with a <property> element.

The many-to-one element is used for "an ordinary association to another persistent class... The relational model is a many-to-one association. (It's really just an object reference.)" In our example, the Employee class would have a many-to-one association to the Department class.

Modifying the mapping file

Our employeeId instance variable will map to the EID column in the database. Because EID will be our primary key, we need to remove the property element that was generated for employeeId and replace it with an id element:

<id name=" employeeId " column="EID"> 
	<generator class="assigned"/> 
</id>

The <id> element's name attribute refers to the name of the JavaBean parameter in our class. The column attribute refers to the column in the database we are mapping to. The generator element's class attribute is set to "assigned," which means that we are going to assign the value of the primary key in the object ourselves. There are other Hibernate options available for auto-generating the primary key. You can find out more about these options in the Hibernate documentation.

Now we need to further modify the code generated by the plugin and go about defining which columns each of the <property> and the <many-to-one> tags are mapped to:

 <property name="email" column="Email"/> 
<property name="firstName" column="FirstName"/> 
<property name="lastName" column="LastName"/> 
 
<many-to-one name="department" column="departmentID"/> 
<many-to-one name="manager" column="managerEID"/>

So our modified document looks like this:

<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC 
  "-//Hibernate/Hibernate Mapping DTD//EN" 
  "http://hibernate.sourceforge.net/hibernate-mapping.dtd"> 
 
<hibernate-mapping> 
  <class name="com.ibm.hibernate_article.Employee" table="employee"> 
 
  	<id name="employeeId" column="EID"> 
  		<generator class="assigned"/> 
  	</id> 
 
  	<property name="email" column="Email"/> 
  	<property name="firstName" column="FirstName"/> 
  	<property name="lastName" column="LastName"/> 
 
  	<many-to-one name="department" column="departmentID"/> 
  	<many-to-one name="manager" column="managerEID"/> 
 
  </class> 
 
</hibernate-mapping>

We'll do essentially the same thing with Department.hbm.xml. The final product looks like this:

<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC 
  "-//Hibernate/Hibernate Mapping DTD//EN" 
  "http://hibernate.sourceforge.net/hibernate-mapping.dtd"> 
 
<hibernate-mapping> 
  <class name="com.ibm.hibernate_article.Department" table="department"> 
      	<id name="departmentID" column="DepartmentID"> 
      			<generator class="assigned"/> 
      		</id> 
      	<property name="city" column="City"/> 
      	<property name="name" column="Name"/> 
      	<property name="state" column="State"/> 
  </class> 
 
</hibernate-mapping>

Create datasources and sessions

We need to load our XML mappings into some sort of object representation so that Hibernate can use them. We achieve this by creating an instance of the cirrus.hibernate.Datastore class. Then we tell the Datastore instance to store mapping information for a given class by calling the storeClass method and providing it with the given class's Class object. The storeClass method knows to use the fully qualified class name to look in the same package for a corresponding .hbm.xml mapping file.

After we have a Datastore object, we need to use it to build a SessionFactory. This SessionFactory will be responsible for creating Session objects. The Hibernate documentation defines a session as "a single-threaded, short-lived object representing a conversation between the application and the persistent store." A session wraps a JDBC connection, acts as a factory for Transaction objects, and manages persistent objects in the application. A session can span several transactions, so it does not necessarily represent an atomic unit of work like a transaction does.

Let's create a static initializer that will be responsible for creating a SessionFactory object. This static initializer will load once when the class is first referenced. After we load this statically, we will no longer need to reload our Employee and Department class mappings.

private SessionFactory sessionFactory; 
 
static { 
	try 
	{ 
		Datastore ds = Hibernate.createDatastore(); 
		ds.storeClass(Employee.class); 
		ds.storeClass(Department.class); 
		sessionFactory = ds.buildSessionFactory(); 
	} 
	catch (Exception e) 
	{ 
		throw new RuntimeException("couldn't get connection"); 
	} 
}

In the code above, the Datastore object gets an instance of SessionFactory by calling the buildSessionFactory method. If no parameters are provided to the buildSessionFactory method, it looks for the default properties file (i.e., the hibernate.properties file we created earlier) in the runtime classpath. Alternatively, a Properties object can be passed in to the buildSessionFactory method if such control of the properties is necessary in the code.

After the static initializer has initialized our SessionFactory object, we can call the openSession() method. This static method will return us a new Session object. The SessionFactory will automatically manage the Connection for you if you call openSession method with no parameters. Many connection parameters such as pool size, statement cache, and idle time are configurable through parameters in the hibernate.properties file (or the properties object provided to the SessionFactory). For more details, see the Hibernate documentation.

If your program already has an existing infrastructure of connection management, you can provide a connection to the openSession(Connection con) method, and Hibernate will use the connection you provide.


Manipulating database objects

This section describes how to write to the database, load objects from the database, and updating and querying the database.

Writing to the database

To write to the database, we will open a new Session using our SessionFactory object. Then we will create the object that we wish to persist and save it in the session. We then flush the session, call commit on the connection, and close the session.

Flushing the session forces Hibernate to synchronize the in-memory data with the database. Hibernate will automatically flush periodically, but there are no guarantees on when this will occur. Thus, we explicitly flush the in-memory data into the database to make sure that it is written immediately.

Before closing the session, you must also make sure to commit the database connection.

Session session = sessionFactory.openSession(); 
 
department = new Department(); 
department.setCity("Austin"); 
department.setState("TX"); 
department.setName("IBM Global Services"); 
department.setDepartmentID(211); 
 
session.save(department); 
session.flush(); 
session.connection().commit(); 
session.close();

Loading an object from the database

Loading an object is the process of bringing an object back into memory by using its identifier. This is different from querying for an object, which we describe in Querying the database.

In order to load an object from the database, we again need a session. We also need the primary key of the object we wish to load. Building off our previous write example, if we want to load our Department back into an object, we would call the session.load method with the Class object representing Department, and our primary key of '211'.

Session session = sessionFactory.openSession(); 
 
Department dept = (Department) session.load(Department.class, new Integer(211)); 
session.close();

Updating the database

You can update an object either in the same session that you created the object, or in an entirely different session. Updating object in the same session is trivial; you just modify the object's state. In order to update an object in a different session, you must load (or query for) the object, and then update it.

Same session

session.save(department); 
 
session.flush(); 
 
department.setName("newName"); 
 
session.flush();

Different session

//first session 
Department department = new Department(); 
department.setDepartmentId(211); 
department.setName("someName"); 
. 
. // set other stuff on department 
. 
 
session.save(department); 
session.flush(); 
session.connection().commit(); 
session.close(); 
 
//later session 
laterSession = sessionFactory.openSession(); 
Department dept = (Department) laterSession.load(Department.class, new Integer(211)); 
dept.setName("aDifferentName"); 
laterSession.flush(); 
laterSession.connection().commit(); 
laterSession.close();

Querying the database

There are a few ways to query the database. The easiest way is to use the session.find method. You must provide the session.find with a query using Hibernate's simple, yet powerful object-oriented query language. The example below demonstrates a fairly simple query. If you wish to do more complex queries, see the Hibernate documentation for more details.

Department department = new Department(); 
department.setDepartmentId(211); 
department.setName("someName"); 
. 
. // set other stuff on department 
. 
 
session.save(department); 
List list = session.find 
("from dept in class com.ibm.hibernate_article.Department where dept.city='Austin' ");

Testing things out

Now that we have a Session object and we know how to do some operations, we can write some CRUD tests on our simple object model to see Hibernate in action. And what better way to test something out than with a JUnit test. Feel free do take a look at the HibernateTest.java testcase that is packaged with the source code for this article.


Conclusion

In this article, we've only scratched the surface of how Hibernate can be used. We presented Hibernate in the context of a few POJOs (Plain Old Java Objects). However, note that the extensive Hibernate API covers more advanced topics such as one-to-many mappings, transactions, and collections. Now that you have your feet wet with Hibernate, you should feel more comfortable about exploring how the open source offering can be used in your programming endeavors.

Using Hibernate on a few projects, we have hit a few roadblocks along the way. We have found the Hibernate forum on SourceForge to be an indispensable source for answers to our questions. The lead developers for Hibernate are very active on the forum and address almost all posts.


Download

DescriptionNameSize
Code samplehibernate.zip  ( HTTP | FTP )5KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Information management on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Information Management
ArticleID=14369
ArticleTitle=Using Hibernate to Persist Your Java Objects to IBM DB2 Universal Database
publish-date=06192003