IBM WebSphere Developer Technical Journal: Developing Hibernate applications for use with WebSphere Application Server

This article provides step by step instructions on using Websphere® Application Server's connection and transaction management when creating Hibernate applications.

Sunil Patil (sdpatil@gmail.com), Software Engineer, BMC

Sunil Patil is a J2EE developer with over six years of experience. His areas of interest includes the Spring Framework, Apache Geronimo, and Portals.


developerWorks Contributing author
        level

15 September 2004

Also available in Chinese

Introduction

Hibernate is a popular open source object relational mapping tool for a Java™ environment. Object relational mapping refers to the technique of mapping the data representation from an object model to a relational data model with a SQL-based schema. What this means is that Hibernate provides you with one more level of abstraction when interacting with a database.

Hibernate is flexible and supports several approaches. On one end, using the minimum subset of the Hibernate API, it can be used simply for interacting with a database, in which case the application will have to provide its own connection and manage its own transactions; which middleware such as WebSphere Application Server takes care of. On the other end, you can use the complete version of Hibernate even if you aren't running middleware, in which case you supply Hibernate with the database configuration information, and it will not only create and manage the connections for you, but it will also manage transactions by delegating them to the underlying database.

To use Hibernate, you create Java classes that represent tables in a database and then map instance variables in a class with columns in the database. You can then call methods on Hibernate to select, insert, update, and delete records in the underlying table, rather than create and execute queries yourself, as you typically would.

Hibernate architecture has three main components:

  • Connection management
    Since opening and closing the connection is the most expensive part of interacting with a database, you will want to pool and reuse your database connection.
  • Transaction management
    A transaction is used when you want to execute more than one query in one batch; the result being either that everything within the transaction succeeds or everything fails.
  • Object relational mapping
    This is the part where a given Java object Hibernate inserts or updates data; for example, when you pass instance of an object to a Session.save() method, Hibernate will read the state of instance variables of that object, and then create and execute the necessary query. In the case of a select query, objects representing ResultSets will be returned.

Hibernate is very flexible and provides different approaches for how these components should be used:

  • "Lite" architecture
    Use when you only want to use the Hibernate object relational mapping component. In this case, you will need to implement connection and transaction management yourself, for example, with WebSphere Application Server.
  • "Full cream" architecture
    Use when you want to implement all three Hibernate components. Hibernate will manage connections for you when you provide it with connection information like driver class name, user name, password, and so on, through XML configuration. When managing transactions, you can call begin, commit, and rollback methods on a Hibernate object.

Hibernate is very good tool when it comes to object relational mapping, but in terms of connection management and transaction management, it is lacking in performance and capabilities. Fortunately, we can mix Hibernate's object relational mapping with the connection and transaction management of WebSphere Application Server to create very powerful application.

This article assumes a basic knowledge of Hibernate, Enterprise JavaBean (EJB) components, and Struts. If you are new to Hibernate, refer to Object relational mapping without container, which discusses how to use Hibernate for simple insert, update, and delete operations.

About the sample application

For purpose of this article, we will create a simple sample Struts Web application that will allow the user to insert new records into a Contact table. Our sample application will use AddContact.jsp for receiving user input. AddContactAction.java is an action class which will use Hibernate for inserting a contact into a database. If the contact insertion is successful, then the user will be redirected to success.jsp, which will show details of the newly inserted contact. If a problem is encountered, then the failure.jsp page will be displayed.

Figure 1. Sample application insert page
Figure 1. Sample application insert page

Using Hibernate

To begin, we will create a sample application which uses Hibernate's own connection pooling and transaction management (also referred to as "full cream" architecture):

  1. Create a Struts application either by using WebSphere Studio Application Developer (hereafter referred as Application Developer), or by importing struts-blank.war, which is included with the Struts distribution. Create the necessary JSPs and action classes for the sample application, or you can import them from the sample1.zip download file.
  2. Download Hibernate binaries from the Hibernate Web site: copy hibernate2.jar and the library JAR files (dom4j.jar,xmlapi.jar, and so on) to the WEB-INF/lib folder.
  3. Create a Contact.java class that will have one member variable each for every column in the Contact table. Getters and setters need to be created for every field. Also, the contact.hbm.xml file needs to be created, which will map instance variables from Contact.java to columns in the Contact table.
  4. Create hibernate.cfg.xml in your source code root directory. This file holds application level configuration information for Hibernate, as shown in Listing 1.

    Listing 1

    <hibernate-configuration>
    <session-factory>
    <property name="hibernate.connection.driver_class">
    COM.ibm.db2.jdbc.app.DB2Driver</property>
    <property name="hibernate.connection.url">jdbc:db2:SAMPLE</property>
    <property name="hibernate.connection.username">db2admin</property>
    <property name="hibernate.connection.password">db2admin</property>
    <property name="hibernate.connection.pool_size">10</property>
    <property name="show_sql">true</property>
    <property name="dialect">net.sf.hibernate.dialect.DB2Dialect</property>
    <property name="hibernate.hbm2ddl.auto">create-drop</property>
    <!-- Mapping files -->
    <mapping resource="contact.hbm.xml"/>
    </session-factory>
    </hibernate-configuration>

    In this file:
    • You will notice that we have to specify the same information here that is required if you were to open the connection using plain JDBC:
      • hibernate.connection.driver_class is a JDBC driver class that we decided to use.
      • hibernate.connection.password and hibernate.connection.username are credentials for the database.
      • hibernate.connection.url JDBC URL for connecting to a database.
    • The dialect property determines the dialect to be used when generating queries. Hibernate supports dialects for all popular Relational Database Management Systems (RDBMS), such as DB2 or Oracle™. Therefore, for example, if you use Oracle during development and want to move to DB2 in production, changes will only be required in the hibernate.cfg.xml file.
    • hibernate.hbm2ddl.auto can be used to instruct Hibernate, when we initialize it, to create DDLs for our application from the mapping files, and create those tables and sequences in the database. The value create-drop means it will drop tables created by Hibernate when Hibernate is closed.
    • contact.hbm.xml is the mapping file for the Contact table. If you have more than one table, you can then either create multiple mappings files (one for each table) and add multiple <mapping> elements, or create multiple mappings in one file and add one <mapping> element.
    Next comes the part in our application where we read values entered by the user and insert a new record into the Contact table. The AddContactAction.java class executes this method:

    Listing 2

    SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
    Session session = sessionFactory.openSession();
    
    //Create new instance of Contact and set values in it by reading them from form object
    Contact contact = new Contact();
    contact.setFirstName((String)contactForm.get("firstName"));
    session.save(contact);
    
    // Actual contact insertion will happen at this step
    session.flush();
    session.close();
  5. Initialize Hibernate by specifying new Configuration().configure().buildSessionFactory(). Hibernate will try to locate /hibernate.cfg.xml at root, and if found will load it, giving us a reference to SessionFactory that we can use to interact with Hibernate. A session indicates one logical interaction; you can fire more than one query in one session.
  6. To insert a contact, create a new instance of Contact class. Set the values of the various fields with the entered contact information by calling the setter method, then pass this contact object to Hibernate for saving by calling session.save(). Hibernate will take care of generating and executing the insert query. When the contact is inserted, the value of the ID field in Contact class will equal the primary key of the newly inserted record in the Contact table. If a problem occurs, Hibernate will throw an exception.

Be sure you copied the JDBC driver JAR files into the WEB-INF/lib folder, otherwise an exception may occur.

To try this example, download the contents of sample1.zip.


Using Hibernate with WebSphere Application Server connection pooling

Based on what we have done above, there is room for improvement in our simple contact management application, and it involves making use of some key features of WebSphere Application Server.

First, in developing the application above we used the connection pooling from Hibernate. This connection pooling algorithm is somewhat rudimentary and not really intended to be used in a production environment, steering you to consider third party connection pooling like Apache's DBCP. We will change our application instead to use WebSphere Application Server's connection pooling:

  1. To use WebSphere Application Server connection pooling, create a JDBC data source with a JNDI name, such as jdbc/ds1. (Refer to the WebSphere Application Servers Information Center for details on configuring a data source.) In hibernate.cfg.xml, remove hibernate.connection.driver_class, the URL and other properties, and add this line:

    <property name="hibernate.connection.datasource">jdbc/ds1</property>

  2. We are creating a SessionFactory object every time in the AddContactAction.java execute method, which is not a good idea since using SessionFactory is expensive to create a thread safe object that is intended to be shared by all application threads. We should create a SessionFactory object only once when the application starts. To do this, we can either create a SessionContextListener class and configure SessionFactory in it or, even better, create a Struts plug-in which will configure SessionFactory, as in Listing 3.

    Listing 3

    public class HibernateStrutsPlugIn implements PlugIn{
    
    	public void init(ActionServlet arg0, ModuleConfig arg1)
    	throws ServletException {
    		try {
    			SessionFactory sessionFactory = new 
    			Configuration().configure().buildSessionFactory();
    		} catch (HibernateException e) {
    				System.out.println("Error in 
    			configuring SessionFactory");
    				e.printStackTrace();
    
    		}
    	}
    
    	// Other methods
    }

    To create a Struts plug-in:
    1. Create a HibernateStrutsPlugin class that will implement the PlugIn interface. In the init() method of this class, call Configuration().configure().buildSessionFactory().
    2. Add an entry for the new plug-in class in struts-config.xml by either using Application Developer (Figure 2) or by or editing struts-config.xml manually and adding the lines in Listing 4 at the end.
      Figure 2. Struts Configuration File Editor
      Figure 2. Struts Configuration File Editor

      Listing 4

      <struts-config>
      <plug-in className="com.sample.util.HibernateStrutsPlugIn">
      </plug-in>
      </struts-config>
    3. We will make one more change to hibernate.cfg.xml that will make reusing SessionFactory easy. By adding the entry below, we tell Hibernate that when we call Configuration().configure() it should create an instance of SessionFactory and bind it with HibernateSessionFactory. When binding the SessionFactory to JNDI, Hibernate will use the values of hibernate.jndi.url and hibernate.jndi.class when initiating InitialContext. If these values are not specified, then default InitialContext values are used.

      <property name="hibernate.session_factory_name"> HibernateSessionFactory</property>

  3. We will update our application and add two new tables: Phone and Address. We also have to change AddContact.jsp to add these two new inputs. The function we want the application to accomplish is if the user specifies either a phone number or an address, a new record will be inserted in the appropriate table. In addition, all this should be part of one transaction, meaning that if, after inserting a contact and phone number, a problem occurs when inserting an address into the database, then all actions associated with the transaction should be rolled back and the failure page should be displayed to the user.

    Create Address.java and Phone.java for representing the Address and Phone tables and map them using address.hbm.xml and contact.hbm.xml. Add <mapping> elements for these two files in hibernate.cfg.xml

    Listing 5

    try {
    	Context ctx = new InitialContext();
    	Object obj = ctx.lookup("HibernateSessionFactory");
    	sessionFactory = (SessionFactory) obj;
    } catch (NamingException e) {
    		e.printStackTrace();
    }
    session = sessionFactory.openSession();
    Transaction contactInsertTransaction = 
    try{
    	session.beginTransaction();
    	//Create new instance of Contact and set values in it by reading them 
    	//from form object
    	Contact contact = new Contact();
    	// Prepare contact object
    	session.save(contact);
    	
    	//Check if value for phone is not null if yes then insert it in db
    	String phoneStr = (String) contactForm.get("phone");
    	if (phoneStr.length() != 0) {
    		//Code for inserting new phone here
    }
    
    	//If you uncomment this line you will find out contact gets inserted
    	//even if there is some problem afterwards\
    	/*	if(true)
    		throw new RuntimeException("Test transaction exception"); */
    	// Code for inserting new Address
    	contactInsertTransaction.commit();
    } catch (HibernateException e) {
    	contactInsertTransaction.rollback();
    	return mapping.findForward("failure");
    }finally{
    	session.flush();
    session.close();
    }

With these changes, we have:

  • Changed the execute method to get the SessionFactory reference from JNDI.
  • Changed the execute method to make the insertion of contact, phone and address one transaction using Hibernate's own transaction management.

If everything goes well, we will commit the transaction before closing the session. If a problem occurs, we will call the rollback() method on the transaction object.


Using Hibernate with WebSphere Application Server transaction management

Second, we now want to change our application so that it can even be accessed by non-Web clients. To do that, we will create a Contact stateless session bean that will expose the addContact() method, and move all our business logic from the AddContactAction class to ContactBean.java.

Since we are moving to a stateless session bean, we have to change how we initialize Hibernate. As soon as JNDI is available for binding objects, Hibernate should be initialized and the SessionFactory object should be bound into JNDI. WebSphere Application Server gives us ability to register for a callback notification when the application server has completed startup.

  1. Create a Java project in Application Developer called sampleListener, and in that project, create HibernateJNDIListener.java, shown in Listing 6.

    Listing 6

    public class HibernateJNDIListener implements CustomService, 
    NotificationListener {
    	public void initialize(Properties arg0) throws 
    	Exception {
    		NotificationFilterSupport filter = new 
    	NotificationFilterSupport();
    		filter.enableType(NotificationConstants.TYPE_J2EE
    	_STATE_RUNNING);
    		ObjectName target = new 
    		ObjectName("WebSphere:*,type=Server");
    		AdminServiceFactory.getAdminService().addNotificationListenerExtended
    			(target,this,filter,null);
    	}
    
    	public void handleNotification(Notification arg0, Object arg1) {
    	try {
    		TestConfiguration.init();
    		System.out.println("Hibernate configured sucessfully");
    		} catch (HibernateException e) {
    			e.printStackTrace();
    		}
    	}
    }
  2. Using the administrative console in WebSphere Application Server, create and register a class (HibernateJNDIListener) that implements the CustomService interface.
    Figure 3. WebSphere administrative console
    Figure 3. WebSphere administrative console

    When the application server or node starts, this class will also start and get control from the initialize() method, which will register an instance of HibernateJNDIListner for receiving TYPE_J2EE_STATE_RUNNING notifications. The HibernateJNDIListner class implements NotificationListener to indicate that it is ready to receive notifications. When WebSphere Application Server's JNDI becomes available for binding objects, it will call the handleNotification() method of HibernateJNDIListener. We will initialize SessionFactory and bind it in JNDI in this method.

  3. To get this working, all Hibernate mapping classes and .hbm.xml files need to be at the same level as sampleListener.jar. Create a new Java project, sampleDB, and move com.sample.db Java files in into this project, along with all .hbm.xml files and hibernate.cfg.xml.
  4. Copy sampleDB.jar and sampleListener.jar into appserver\lib, along with the following:
    • hibernate2.jar
    • do dom4j.jar
    • cgilib-full-2.0.1.jar
    • xml-apis.jar
    • commons-collection-2.1.jar
    • ecache-0.7.jar.
  5. Start WebSphere Application Server and check systemout.log for a message that indicates that Hibernate configured successfully.
  6. We will now change our application to use WebSphere Application Server transaction management so that the contact information we added becomes part of an existing transaction. For example, after the new contact information is inserted, we might want to send a confirmation e-mail to the user. If we already have a message-driven bean for sending the e-mail, this additional information should be part of one transaction. We can hook into WebSphere Application Server transaction management by adding this entry in hibernate.cfg.xml

    <property name="net.sf.hibernate.transaction.JTATransactionFactory"> net.sf.hibernate.transaction.WebSphereTransactionManagerLookup</property>

We do not have to specify transaction boundaries manually in this example. WebSphere Application Server will start a new transaction when it calls the addContact() method. If the method completes execution normally without throwing an exception, then the transaction will be committed or else rolled back.


Conclusion

When using Hibernate, use the complete architecture when you do not have an application server; for example, you have a SWING application that interacts with a database. Hibernate in this case can save you lot of low level plumbing. However, if you do run middleware, use a data source exposed by WebSphere Application Server since it is optimized for performance, and also because you can have more than one application deployed that shares the same connection pool.

For enterprise applications, you will have databases, a messaging system, and various EIS systems, and you will want to manipulate all necessary systems in one transaction. In this case, you should use the Java Transaction API (JTA) provided by WebSphere Application Server in Hibernate; when Hibernate manages transactions, it does so by delegating calls to the underlying database, and so it would be a problem when it came across systems other than databases.


Downloads

DescriptionNameSize
Code samplesample1.zip  ( HTTP | FTP )5.0 MB
Code samplesample2.zip  ( HTTP | FTP )5.0 MB
Code samplesample3.zip  ( HTTP | FTP )5.6 MB

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 WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=16286
ArticleTitle=IBM WebSphere Developer Technical Journal: Developing Hibernate applications for use with WebSphere Application Server
publish-date=09152004