Maven by Example

7.5. The Simple Persist Module

This module defines two very simple Data Access Objects (DAOs). A DAO is an object that provides an interface for persistence operations. In an application that makes use of an Object-Relational Mapping (ORM) framework such as Hibernate, DAOs are usually defined around objects. In this project, we are defining two DAO objects: WeatherDAO and LocationDAO. The WeatherDAO class allows us to save a Weather object to a database and retrieve a Weather object by id, and to retrieve Weather objects that match a specific Location. The LocationDAO has a method that allows us to retrieve a Location object by zip code. First, let’s take a look at the simple-persist POM in simple-persist POM.

simple-persist POM. 

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.sonatype.mavenbook.multispring</groupId>
        <artifactId>simple-parent</artifactId>
        <version>1.0</version>
    </parent>
    <artifactId>simple-persist</artifactId>
    <packaging>jar</packaging>

    <name>Simple Persistence API</name>

    <dependencies>
        <dependency>
            <groupId>org.sonatype.mavenbook.multispring</groupId>
            <artifactId>simple-model</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate</artifactId>
            <version>3.2.5.ga</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.transaction</groupId>
                    <artifactId>jta</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-annotations</artifactId>
            <version>3.3.0.ga</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-commons-annotations</artifactId>
            <version>3.3.0.ga</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
            <version>2.0.7</version>
        </dependency>
    </dependencies>
</project>

This POM file references simple-parent as a parent POM, and it defines a few dependencies. The dependencies listed in simple-persist's POM are:

org.sonatype.mavenbook.multispring:simple-model:1.0
Just like the simple-weather module, this persistence module references the core model objects defined in simple-model.
org.hibernate:hibernate:3.2.5.ga
We define a dependency on Hibernate version 3.2.5.ga, but notice that we’re excluding a dependency of Hibernate. We’re doing this because the javax.transaction:jta dependency is not available in the public Maven repository. This dependency happens to be one of those Sun dependencies that has not yet made it into the free central Maven repository. To avoid an annoying message telling us to go download these nonfree dependencies, we simply exclude this dependency from Hibernate.
javax.servlet:servlet-api:2.4
Since this project contains a Servlet, we need to include the Servlet API version 2.4.
org.springframework:spring:2.0.7
This includes the entire Spring Framework as a dependency. It is generally a good practice to depend on only the components of Spring you happen to be using. The Spring Framework project has been nice enough to create focused artifacts such as spring-hibernate3.

Why depend on Spring? When it comes to Hibernate integration, Spring allows us to leverage helper classes such as HibernateDaoSupport. For an example of what is possible with the help of HibernateDaoSupport, take a look at the code for the WeatherDAO in simple-persist’s WeatherDAO Class.

simple-persist’s WeatherDAO Class. 

package org.sonatype.mavenbook.weather.persist;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import org.sonatype.mavenbook.weather.model.Location;
import org.sonatype.mavenbook.weather.model.Weather;

public class WeatherDAO extends HibernateDaoSupport { (1)

    public WeatherDAO() {}

    public void save(Weather weather) { (2)
            getHibernateTemplate().save( weather );
    }

    public Weather load(Integer id) { (3)
            return (Weather) getHibernateTemplate().load( Weather.class, id);
    }

    @SuppressWarnings("unchecked")
        public List<Weather> recentForLocation( final Location location ) {
        return (List<Weather>) getHibernateTemplate().execute(
                                                              new HibernateCallback() { (4)
                                                                  public Object doInHibernate(Session session) {
                                                                  Query query = getSession().getNamedQuery("Weather.byLocation");
                                                                  query.setParameter("location", location);
                                                                  return new ArrayList<Weather>( query.list() );
                                                              }
                                                              });
    }
}

That’s it. No really, you are done writing a class that can insert new rows, select by primary key, and find all rows in Weather that join to an id in the Location table. Clearly, we can’t stop this book and insert the five hundred pages it would take to get you up to speed on the intricacies of Hibernate, but we can do some very quick explanation:

(1)

This class extends HibernateDaoSupport. What this means is that the class is going to be associated with a Hibernate SessionFactory which it is going to use to create Hibernate Session objects. In Hibernate, every operation goes through a Session object, a Session mediates access to the underlying database and takes care of managing the connection to the JDBC DataSource. Extending HibernateDaoSupport also means that we can access the HibernateTemplate using getHibernateTemplate(). For an example of what can be done with the HibernateTemplate

(2)

The save() method takes an instance of Weather and calls the save() method on a HibernateTemplate. The HibernateTemplate simplifies calls to common Hibernate operations and converts any database specific exceptions to runtime exceptions. Here we call out to save() which inserts a new record into the Weather table. Alternatives to save() are update() which updates an existing row, or saveOrUpdate() which would either save or update depending on the presence of a non-null id property in Weather.

(3)

The load() method, once again, is a one-liner that just calls a method on an instance of HibernateTemplate. load() on HibernateTemplate takes a Class object and a Serializable object. In this case, the Serializable corresponds to the id value of the Weather object to load.

(4)

This last method recentForLocation() calls out to a NamedQuery defined in the Weather model object. If you can think back that far, the Weather model object defined a named query "Weather.byLocation" with a query of "from Weather w where w.location = :location". We’re loading this NamedQuery using a reference to a Hibernate Session object inside a HibernateCallback which is executed by the execute() method on HibernateTemplate. You can see in this method that we’re populating the named parameter location with the parameter passed in to the recentForLocation() method.

Now is a good time for some clarification. HibernateDaoSupport and HibernateTemplate are classes from the Spring Framework. They were created by the Spring Framework to make writing Hibernate DAO objects painless. To support this DAO, we’ll need to do some configuration in the simple-persist Spring ApplicationContext definition. The XML document shown in Spring Application Context for simple-persist is stored in src/main/resources in a file named applicationContext-persist.xml.

Spring Application Context for simple-persist. 

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
       default-lazy-init="true">

    <bean id="sessionFactory"
          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="annotatedClasses">
            <list>
                <value>org.sonatype.mavenbook.weather.model.Atmosphere</value>
                <value>org.sonatype.mavenbook.weather.model.Condition</value>
                <value>org.sonatype.mavenbook.weather.model.Location</value>
                <value>org.sonatype.mavenbook.weather.model.Weather</value>
                <value>org.sonatype.mavenbook.weather.model.Wind</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.transaction.factory_class">
                    org.hibernate.transaction.JDBCTransactionFactory
                </prop>
                <prop key="hibernate.dialect">
                    org.hibernate.dialect.HSQLDialect
                </prop>
                <prop key="hibernate.connection.pool_size">0</prop>
                <prop key="hibernate.connection.driver_class">
                    org.hsqldb.jdbcDriver
                </prop>
                <prop key="hibernate.connection.url">
                    jdbc:hsqldb:data/weather;shutdown=true
                </prop>
                <prop key="hibernate.connection.username">sa</prop>
                <prop key="hibernate.connection.password"></prop>
                <prop key="hibernate.connection.autocommit">true</prop>
                <prop key="hibernate.jdbc.batch_size">0</prop>
            </props>
        </property>
    </bean>

    <bean id="locationDAO"
          class="org.sonatype.mavenbook.weather.persist.LocationDAO">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <bean id="weatherDAO"
          class="org.sonatype.mavenbook.weather.persist.WeatherDAO">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
</beans>

In this application context, we’re accomplishing a few things. The sessionFactory bean is the bean from which the DAOs retrieve Hibernate Session objects. This bean is an instance of AnnotationSessionFactoryBean and is supplied with a list of annotatedClasses. Note that the list of annotated classes is the list of classes defined in our simple-model module. Next, the sessionFactory is configured with a set of Hibernate configuration properties (hibernateProperties). In this example, our Hibernate properties define a number of settings:

hibernate.dialect
This setting controls how SQL is to be generated for our database. Since we are using the HSQLDB database, our database dialect is set to org.hibernate.dialect.+HSQLDialect+. Hibernate has dialects for all major databases such as Oracle, MySQL, Postgres, and SQL Server.
hibernate.connection.*
In this example, we’re configuring the JDBC connection properties from the Spring configuration. Our applications are configured to run against a HSQLDB in the ./data/weather directory. In a real enterprise application, it is more likely you would use something like JNDI to externalize database configuration from your application’s code.

Lastly, in this bean definition file, both of the simple-persist DAO objects are created and given a reference to the sessionFactory bean just defined. Just like the Spring application context in simple-weather, this applicationContext-persist.xml file defines the architecture of a submodule in a larger enterprise design. If you were working with a larger collection of persistence classes, you might find it useful to capture them in an application context which is separate from your application.

There’s one last piece of the puzzle in simple-persist. Later in this chapter, we’re going to hibernate.cfg.xml in src/main/resources. The purpose of this file (which duplicates some of the configuration in applicationContext-persist.xml) is to allow us to leverage the Maven Hibernate3 plugin to generate Data Definition Language (DDL) from nothing more than our annotations. See simple-persist hibernate.cfg.xml.

simple-persist hibernate.cfg.xml. 

<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.HSQLDialect</property>

        <!-- Database connection settings -->
        <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
        <property name="connection.url">jdbc:hsqldb:data/weather</property>
        <property name="connection.username">sa</property>
        <property name="connection.password"></property>
        <property name="connection.shutdown">true</property>

        <!-- JDBC connection pool (use the built-in one) -->
        <property name="connection.pool_size">1</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">
            org.hibernate.cache.NoCacheProvider
        </property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- disable batching so HSQLDB will propagate errors correctly. -->
        <property name="jdbc.batch_size">0</property>

        <!-- List all the mapping documents we're using -->
        <mapping class="org.sonatype.mavenbook.weather.model.Atmosphere"/>
        <mapping class="org.sonatype.mavenbook.weather.model.Condition"/>
        <mapping class="org.sonatype.mavenbook.weather.model.Location"/>
        <mapping class="org.sonatype.mavenbook.weather.model.Weather"/>
        <mapping class="org.sonatype.mavenbook.weather.model.Wind"/>

    </session-factory>
</hibernate-configuration>

The contents of Spring Application Context for simple-persist and simple-parent Project POM are redundant. While the Spring Application Context XML is going to be used by the web application and the command-line application, the hibernate.cfg.xml exists only to support the Maven Hibernate3 plugin. Later in this chapter, we’ll see how to use this hibernate.cfg.xml and the Maven Hibernate3 plugin to generate a database schema based on the annotated object model defined in simple-model. This hibernate.cfg.xml file is the file that will configure the JDBC connection properties and enumerate the list of annotated model classes for the Maven Hibernate3 plugin.