Chapter 1. Quickstart with Tomcat

1.1. Getting started with Hibernate

This tutorial discusses a setup of Hibernate 2.1 with the Apache Tomcat servlet container for a web-based application. Hibernate works well in a managed environment with all major J2EE application servers, or in standalone applications. The database system in this example is PostgreSQL 7.3, but this can be easily changed to any of the other 16 Hibernate supported databases.

The first step is to copy all required libraries to the Tomcat installation. We use a separate web context (webapps/quickstart) for this tutorial, so we've to consider both the global library search path (TOMCAT/common/lib) and the classloader at the context level in webapps/quickstart/WEB-INF/lib (for JAR files) and webapps/quickstart/WEB-INF/classes. We refer to both classloader levels as the global classpath and the context classpath.

  1. First, copy the JDBC driver for the database to the global classpath. This is required for the DBCP connection pool software which comes bundled with Tomcat, For this tutorial, copy the pg73jdbc3.jar library (for PostgreSQL 7.3 and JDK 1.4) to the global classloaders path. If you'd want to use a different database, simply copy its appropriate JDBC driver.

  2. Never copy anthing else into the global classloader path in Tomcat, or you will get problems with various tools, including Log4j, commons-logging and others. Always us the context classpath for each web application, that is, copy libraries to WEB-INF/lib and your own builds and configuration/property files to WEB-INF/classes. Both directories are in the context level classpath by default.

  3. Hibernate is packaged as a JAR library. The hibernate2.jar file is to be placed in the context classpath together with other classes of the application. Hibernate requires some 3rd party libraries at runtime, these come bundled with the Hibernate distribution in the lib/ directory; see Table 1.1. Copy the required 3rd party libraries to the context classpath.

  4. Configure both Tomcat and Hibernate for a database connection. This means Tomcat will provide pooled JDBC connections, Hibernate requests theses connections through JNDI. Tomcat binds the connection pool to JNDI.

Table 1.1.  Hibernate 3rd party libraries

Library Description
dom4j (required) Hibernate uses dom4j to parse XML configuration and XML mapping metadata files.
CGLIB (required) Hibernate uses the code generation library to enhance classes at runtime (in combination with Java reflection).
Commons Beanutils, Commons Collections, Commons Lang, Commons Logging (required) Hibernate uses the various utility libraries from the Apache Jakarta Commons project.
ODMG4 (required) Hibernate provides an optional ODMG compliant persistence manager interface. It is required if you like to map collections, even if you don't intend to use the ODMG API. We don't map collections in this tutorial, but it's a good idea to copy the JAR anyway.
Log4j (optional) Hibernate uses the Commons Logging API, which in turn can use Log4j as the logging mechanism. If the Log4j library is placed in the context library directory, Commons Logging will use Log4j and its log4j.properties in the context classpath. An example properties file for log4j is delivered with the Hibernate distribution. So, copy log4j.jar to your context classpath too.
Required or not? Have a look at the file lib/README.txt in the Hibernate distribution. This is an up-to-date list of 3rd party libraries distributed with Hibernate. You will find all required and optional libraries listed there.

After all libraries have been copied, a resource declaration for the database JDBC connection pool has to be added to Tomcats main configuration file, TOMCAT/conf/server.xml:

<Context path="/quickstart" docBase="quickstart">
    <Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
    <ResourceParams name="jdbc/quickstart">
        <parameter>
            <name>factory</name>
            <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
        </parameter>

        <!-- DBCP database connection settings -->
        <parameter>
            <name>url</name>
            <value>jdbc:postgresql://localhost/quickstart</value>
        </parameter>
        <parameter>
            <name>driverClassName</name><value>org.postgresql.Driver</value>
        </parameter>
        <parameter>
            <name>username</name>
            <value>quickstart</value>
        </parameter>
        <parameter>
            <name>password</name>
            <value>secret</value>
        </parameter>

        <!-- DBCP connection pooling options -->
        <parameter>
            <name>maxWait</name>
            <value>3000</value>
        </parameter>
        <parameter>
            <name>maxIdle</name>
            <value>100</value>
        </parameter>
        <parameter>
            <name>maxActive</name>
            <value>10</value>
        </parameter>
    </ResourceParams>
</Context>

The context we configure in this example is named quickstart, its base is the TOMCAT/webapp/quickstart directory. To access any Servlets, call the path http://localhost:8080/quickstart in your browser.

Tomcat uses the DBCP connection pool with this configuration and provides pooled JDBC Connections through JNDI at java:comp/env/jdbc/quickstart. If you have trouble getting the connection pool running, refer to the Tomcat documentation. If you get JDBC driver exception messages, try to setup JDBC connection pool without Hibernate first. Tomcat & JDBC tutorials are available on the Web.

The next step is to configure Hibernate, using the connections from the JNDI bound pool. We use Hibernates XML based configuration. The basic approach, using properties, is equivalent in features, but doesn't offer any advantages. We use the XML configuration because it is usualy more convenient. The XML configuration file is placed in the context classpath (WEB-INF/classes), as 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="connection.datasource">java:comp/env/jdbc/quickstart</property>
        <property name="show_sql">false</property>
        <property name="dialect">net.sf.hibernate.dialect.PostgreSQLDialect</property>

        <!-- Mapping files -->
        <mapping resource="Cat.hbm.xml"/>

    </session-factory>

</hibernate-configuration>

We turn logging of SQL commands off and tell Hibernate what database SQL dialect is used and where to get the JDBC connections (by declaring the JNDI address where the datasource pool is bound). The dialect is a required setting, databases differ in their interpretation of the SQL "standard". Hibernate will take care of the differences and comes bundled with dialects for all major commercial and open source databases.

A SessionFactory is Hibernates concept of a single datastore, multiple databases can be used by creating multiple XML configuration files and creating multiple Configuration and SessionFactory objects in your application.

The last element of the hibernate.cfg.xml declares Cat.hbm.xml as the name of a Hibernate XML mapping file for the persistent class Cat. This file contains the metadata for the mapping of the POJO class to a datbase table (or multiple tables). We'll come back to that file soon. Let's write the POJO class first and then declare the mapping metadata for it.

1.2. First persistent class

Hibernate facilitates Plain Old Java Objects (POJOs, sometimes called Plain Ordinary Java Objects) for persistent classes. A POJO is much like a JavaBean, with properties of the class accessible via getter and setter methods, thus shielding the internal representation from the publicly visible type:

package net.sf.hibernate.examples.quickstart;

public class Cat {

    private String id;
    private String name;
    private char sex;
    private float weight;

    public Cat() {
    }

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public float getWeight() {
        return weight;
    }

    public void setWeight(float weight) {
        this.weight = weight;
    }

}

Hibernate is not restricted in its usage of property types, all Java JDK types and primitives (like String, char and float) can be mapped, including classes from the Java collections framework. You can map them as values, collections of values, or associations to other entities. The id is a special property that represents the database identifer (primary key) of that class, it is mandatory for entities like a Cat.

No special interface has to be implemented for persistent classes nor do we have to subclass from a special root persistent class. Hibernate also doesn't use any build time processing, such as byte-code manipulation, it relies solely on Java reflection and runtime class enhancement (through CGLIB). So, without any dependency in the POJO class on Hibernate, we can map it to a database table.

1.3. Mapping the cat

The Cat.hbm.xml mapping file contains the metadata required for the object/relational mapping.

The metadata includes declaration of persistent classes and the mapping of properties (as values or associations to other entities) to database tables.

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

<hibernate-mapping>

    <class name="net.sf.hibernate.examples.quickstart.Cat" table="CAT">

        <!-- A 32 hex character is our surrogate key. It's automatically
            generated by Hibernate with the UUID pattern. -->
        <id name="id" type="string" unsaved-value="null" >
            <column name="CAT_ID" sql-type="char(32)" not-null="true"/>
            <generator class="uuid.hex"/>
        </id>

        <!-- A cat has to have a name, but it shouldn' be too long. -->
        <property name="name">
            <column name="NAME" sql-type="varchar(16)" not-null="true"/>
        </property>

        <property name="sex"/>

        <property name="weight"/>

    </class>

</hibernate-mapping>

Every persistent class has to have an identifer attribute (actually, only classes representing first class objects, not dependent value objects, which are mapped as components of a first class object). This property is used to distinguish persistent objects: Two cats are equal if catA.getId().equals(catB.getId()) is true, this concept is called database identity. Hibernate comes bundled with various identifer generators for different scenarios (including native generators for database sequences and hi/lo identifier patterns). We use the UUID generator and also specify the column CAT_ID of the table CAT for the generated identifier value (as a primary key of the table).

All other properties of Cat are mapped to the same table. In the case of the name property, we mapped it with an explicit database column declaration. This is especially useful when the database schema is automatically generated (as SQL DDL statements) from the mapping declaration with Hibernate's SchemaExport tool. All other properties are mapped using Hibernate's default settings, which is what you need most of the time. The table CAT in the database looks like this:

 Column |         Type          | Modifiers
--------+-----------------------+-----------
 cat_id | character(32)         | not null
 name   | character varying(16) | not null
 sex    | character(1)          |
 weight | real                  |
Indexes: cat_pkey primary key btree (cat_id)

You should now create this table in your database manually, and later read Chapter 19, Toolset Guide if you want to automate this step with the SchemaExport tool. This tool can create a full SQL DDL, including table definition, custom column type constraints, unique constraints and indexes.

1.4. Playing with cats

We're now ready to start Hibernate's Session. We use it to store and retrieve Cats from the database. But first, we've to get a Session (Hibernate's unit-of-work) from the SessionFactory:

SessionFactory sessionFactory =
            new Configuration().configure().buildSessionFactory();

A SessionFactory is responsible for one database and may only use one XML configuration file (hibernate.cfg.xml).

The focus of this tutorial is the setup of Tomcat for JNDI bound JDBC connections, and a basic Hibernate configuration. You can write a Servlet containing the following code any way you like, just make sure that a SessionFactory is only created once. This means you should not keep it in an instance variable in your Servlet. A good choice is a static SessionFactory in a helper class like this:

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

public class HibernateUtil {

    private static final SessionFactory sessionFactory;

    static {
        try {
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (HibernateException ex) {
            throw new RuntimeException("Exception building SessionFactory: " + ex.getMessage(), ex);
        }
    }

    public static final ThreadLocal session = new ThreadLocal();

    public static Session currentSession() throws HibernateException {
        Session s = (Session) session.get();
        // Open a new Session, if this Thread has none yet
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);
        }
        return s;
    }

    public static void closeSession() throws HibernateException {
        Session s = (Session) session.get();
        session.set(null);
        if (s != null)
            s.close();
    }
}

This class does not only take care of the SessionFactory with its static attribute, but also has a ThreadLocal to hold the Session for the current executing thread.

A Session is a non-threadsafe object that represents a single unit-of-work with the database. Sessions are opened by a SessionFactory and are closed when all work is completed:

Session session = HibernateUtil.currentSession();

Transaction tx= session.beginTransaction();

Cat princess = new Cat();
princess.setName("Princess");
princess.setSex('F');
princess.setWeight(7.4f);

session.save(princess);
tx.commit();

HibernateUtil.closeSession();

In a Session, every database operation happens inside a transaction, which isolates the operations (even read-only operations). We use Hibernates Transaction API to abstract from the underlying transaction strategy (in our case, JDBC transactions). This allows our application to be deployed with container managed transactions (using JTA) without any change in the source code, if so desired. Please note that the example above does not handle any exceptions.

Also note that you may call HibernateUtil.currentSession(); as many times as you like, you will always get the current Session of this thread. You have to make sure the Session is closed after your database transaction(s), either in your Servlet code or in a ServletFilter before the HTTP response is send.

Hibernate has various methods that can be used to retrieve objects from the database. The most flexible way is using the Hibernate Query Language (HQL), which is an easy to learn and powerful object-oriented extension to SQL:

Transaction tx= session.beginTransaction();

Query query = session.createQuery("select cat from Cat as cat where cat.sex = :sex");
query.setCharacter("sex", 'F');
for (Iterator it = query.iterate(); it.hasNext();) {
    Cat cat = (Cat) it.next();
    out.println("Female Cat: " + cat.getName() );
}

tx.commit();

Hibernate also offers an object-oriented query by criteria API that can be used to formulate type-safe queries. Hibernate of course uses PreparedStatements and parameter binding for all SQL communication with the database.

1.5. Finally

We only scratched the surface of Hibernate in this small tutorial. Please note that we don't include any Servlet specific code in our examples. You have to create a Servlet yourself and insert the Hibernate code as you see fit.

Keep in mind that Hibernate, as a data access layer, is tightly integrated into your application. Usually, all other layers depent on the persistence mechanism. Make sure you understand the implications of this design.