Chapter 7. Multi-module Enterprise Project

7.1. Introduction

Favicon

In this chapter, we create a multi-module project which evolves the examples from Chapter 6, A Multi-module Project and Chapter 5, A Simple Web Application into a project which uses the Spring Framework and Hibernate to create both a simple web application and a command-line utility to read data from the Yahoo! Weather feed. The simple-weather code developed in Chapter 4, Customizing a Maven Project will be combined with the simple-webapp project defined in Chapter 5, A Simple Web Application. In the process of creating this multi-module project, we'll explore Maven and discuss the different ways it can be used to create modular projects which encourage reuse.

7.1.1. Downloading this Chapter's Example

Favicon

The multi-module project developed in this example consists of modified versions of the projects developed in Chapter 4, Customizing a Maven Project and Chapter 5, A Simple Web Application, and we are not using the Maven Archetype plug-in to generate this multi-module project. We strongly recommend downloading a copy of the example code to use as a supplemental reference while reading the content in this chapter. Without the examples, you won't be able to recreate this chapter's example code. This chapter's example project may be downloaded with the book's example code at http://www.sonatype.com/book/mvn-examples-1.0.zip or http://www.sonatype.com/book/mvn-examples-1.0.tar.gz. Unzip this archive in any directory, and then go to the ch07/ directory. In the ch07/ directory you will see a directory named simple-parent/ which contains the multi-module Maven project developed in this chapter. In the simple-parent/ project directory you will see a pom.xml and the five submodule directories simple-model/, simple-persist/, simple-command/, simple-weather/ and simple-webapp/. If you wish to follow along with the example code in a web browser, go to http://www.sonatype.com/book/examples-1.0 and click on the ch07/ directory.

7.1.2. Multi-module Enterprise Project

Favicon

Presenting the complexity of a massive Enterprise-level project far exceeds the scope of this book. Such projects are characterized by multiple databases, integration with external systems, and subprojects which may be divided by departments. These projects usually span thousands of lines of code, and involve the effort of tens or hundreds of software developers. While such a complete example is outside the scope of this book, we can provide you with a sample project that suggests the complexity of larger Enterprise application. In the conclusion we suggest some possibilities for modularity beyond that presented in this chapter.

In this chapter, we're going to look at a multi-module Maven project that will produce two applications: a command-line query tool for the Yahoo! Weather feed and a web application which queries the Yahoo! Weather feed. Both of these applications will store the results of queries in an embedded database. Each will allow the user to retrieve historical weather data from this embedded database. Both applications will reuse application logic and share a persistence library. This chapter's example builds upon the Yahoo! Weather parsing code introduced in Chapter 4, Customizing a Maven Project. This project is divided into five submodules shown in Figure 7.1, “Multi-module Enterprise Application Module Relationships”.

Multi-module Enterprise Application Module Relationships

Figure 7.1. Multi-module Enterprise Application Module Relationships


In Figure 7.1, “Multi-module Enterprise Application Module Relationships”, you can see that there are five submodules of simple-parent, they are:

simple-model

This module defines a simple object model which models the data returned from the Yahoo! Weather feed. This object model contains the Weather, Condition, Atmosphere, Location, and Wind objects. When our application parses the Yahoo! Weather feed, the parsers defined in simple-weather will parse the XML and create Weather objects which are then used by the application. This project contains model objects annotated with Hibernate 3 Annotations which are used by the logic in simple-persist to map each model object to a corresponding table in a relational database.

simple-weather

This module contains all of the logic required to retrieve data from the Yahoo! Weather feed and parse the resulting XML. The XML returned from this feed is converted into the model objects defined in simple-model. simple-weather has a dependency on simple-model. simple-weather defines a WeatherService object which is referenced by both the simple-command and simple-webapp projects.

simple-persist

This module contains some Data Access Objects (DAO) which are configured to store Weather objects in an embedded database. Both of the applications defined in this multi-module project will use the DAOs defined in simple-persist to store data in an embedded database. The DAOs defined in this project understand and return the model objects defined in simple-model. simple-persist has a direct dependency on simple-model and it depends upon the Hibernate Annotations present on the model objects.

simple-webapp

The web application project contains two Spring MVC Controller implementations which use the WeatherService defined in simple-weather and the DAOs defined in simple-persist. simple-webapp has a direct dependency on simple-weather and simple-persist; it has a transitive dependency on simple-model.

simple-command

This module contains a simple command-line tool which can be used to query the Yahoo! Weather feed. This project contains a class with a static main() function and interacts with the WeatherService defined in simple-weather and the DAOs defined in simple-persist. simple-command has a direct dependency on simple-weather and simple-persist; is has a transitive dependency on simple-model.

This chapter contains a contrived example, simple enough to introduce in a book, yet complex enough to justify a set of five submodules. While our contrived example has a model project with five classes, a persistence library with two service classes, and a weather parsing library with five or six classes, a real-world system might have a model project with one hundred objects, several persistence libraries and service libraries spanning multiple departments. While we've tried to make sure that the code contained in this example is straightforward enough to comprehend in a single sitting, we've also gone out of our way to build a modular project. You might be tempted to look at the examples in this chapter and walk away with the idea that Maven encourages too much complexity given that our model project only has five classes. While using Maven does suggest a certain level of modularity, do realize that we've gone out of our way to complicate our simple example projects for the purpose of demonstrating Maven's multi-module features.

7.1.3. Technology Used in this Example

Favicon

This chapter's example involves some technology which, while popular, is not directly related to Maven. These technologies are the Spring Framework and Hibernate. The Spring Framework is an Inversion of Control (IoC) container and a set of frameworks that aim to simplify interaction with various J2EE libraries. Using the Spring Framework as a foundational framework for application development gives you access to a number of helpful abstractions that can take much of the meddlesome busywork out of dealing with persistence frameworks like Hibernate or iBatis or enterprise APIs like JDBC, JNDI, and JMS. The Spring Framework has grown in popularity over the past few years as a replacement for the heavy weight enterprise standards coming out of Sun Microsystems. Hibernate is a widely used Object-Relational Mapping framework which allows you to interact with a relational database as if it were a collection of Java objects. This example focuses on building a simple web application and a command-line application that uses the Spring Framework to expose a set of reusable components to applications and which also uses Hibernate to persist weather data in an embedded database.

We've decided to include references to these frameworks to demonstrate how one would construct projects using these technologies when using Maven. While there are brief efforts to introduce these technologies throughout this chapter, this chapter will not go out of its way to fully explain these technologies to the reader. For more information about the Spring Framework please see the project's web site at http://www.springframework.org/. For more information about Hibernate and Hibernate Annotations, please see the project's web site at http://www.hibernate.org. This chapter uses HSQLDB as an embedded database, for more information about this database see the project's web site at http://hsqldb.org/.

7.2. The Simple Parent Project

Favicon

This simple-parent project has a pom.xml which references five sub-modules: simple-command, simple-model, simple-weather, simple-persist, and simple-webapp. The top-level pom.xml is shown in Example 7.1, “simple-parent Project POM”.

Example 7.1. simple-parent Project 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>

  <groupId>org.sonatype.mavenbook.ch07</groupId>
  <artifactId>simple-parent</artifactId>
  <packaging>pom</packaging>
  <version>1.0</version>
  <name>Chapter 7 Simple Parent Project</name>
 
  <modules>
    <module>simple-command</module>
    <module>simple-model</module>
    <module>simple-weather</module>
    <module>simple-persist</module>
    <module>simple-webapp</module>
  </modules>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
            <source>1.5</source>
            <target>1.5</target>
          </configuration>
        </plugin>
      </plugins>
   </pluginManagement> 
  </build>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>


Note the similarities of this parent POM to the parent POM defined in Example 6.1, “simple-parent Project POM”. The only real difference between these two POMs is the list of submodules. Where that example only listed two submodules, this parent POM lists five submodules. The next few sections explore each of these five submodules in some detail. Because our example uses Java annotations, we've configured the compiler to target the Java 5 JVM.

7.3. The Simple Model Module

Favicon

The first thing most enterprise projects need is an object model. An object model captures the core set of domain objects in any system. A banking system might have an object model which consists of an Account, Customer, and Transaction object, or a system to capture and communicate sports scores might have a Team and a Game object. Whatever it is, there's a good chance that you've modeled the concepts in your system in an object model. It is a common practice in Maven projects to separate this project into a separate project which is widely referenced. In this system we are capturing each query to the Yahoo! Weather feed with a Weather object which references four other objects. Wind direction, chill, and speed are stored in a Wind object. Location data including the zip code, city, region, and country are stored in a Location class. Atmospheric conditions such as the humidity, maximum visibility, barometric pressure, and whether the pressure is rising or falling is stored in an Atmosphere class. A textual description of conditions, the temperature, and the date of the observation is stored in a Condition class.

Simple Object Model for Weather Data

Figure 7.2. Simple Object Model for Weather Data


The pom.xml file for this simple model object contains one dependency which bears some explanation. Our object model is annotated with Hibernate Annotations. We use these annotations to map the model objects in this model to tables in a relational database. The dependency is org.hibernate:hibernate-annotations:3.3.0.ga. Take a look at the pom.xml, and then see the next few examples for some examples of these annotations.

Example 7.2. simple-model pom.xml

<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.ch07</groupId>
    <artifactId>simple-parent</artifactId>
    <version>1.0</version>
  </parent>
  <artifactId>simple-model</artifactId>
  <packaging>jar</packaging>

  <name>Simple Object Model</name>

  <dependencies>
    <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>
  </dependencies>
</project>

In src/main/java/org/sonatype/mavenbook/weather/model we have Weather.java which contains the annotated Weather model object. The Weather object is a simple Java Bean, this means that we have private member variables like id, location, condition, wind, atmosphere, and date exposed with public getter and setter methods that adhere to the following pattern: if a property is named "name", there will be a public no-arg getter method named getName(), and there will be a one-argument setter named setName(String name). While we show the getter and setter method for the id property, we've omitted most of the getters and setters for most of the other properties to save a few trees.


Example 7.3. Annotated Weather Model Object

package org.sonatype.mavenbook.weather.model;

import javax.persistence.*;

import java.util.Date;

@Entity
@NamedQueries({
  @NamedQuery(name="Weather.byLocation", 
              query="from Weather w where w.location = :location")
})
public class Weather {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;

    @ManyToOne(cascade=CascadeType.ALL)
    private Location location;

    @OneToOne(mappedBy="weather",cascade=CascadeType.ALL)
    private Condition condition;

    @OneToOne(mappedBy="weather",cascade=CascadeType.ALL)
    private Wind wind;

    @OneToOne(mappedBy="weather",cascade=CascadeType.ALL)
    private Atmosphere atmosphere;

    private Date date;
    
    public Weather() {}

    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }

    // All getter and setter methods omitted...
}

In the Weather class, we are using Hibernate annotations to provide guidance to the simple-persist project. These annotations are used by Hibernate to map an object to a table in a relational database. While a full explanation of Hibernate annotations is beyond the scope of this chapter, here is some brief explanation for the curious. The @Entity annotation marks this class as a persistent entity, we've omitted the @Table annotation on this class, so Hibernate is going to use the name of the class as the name of the table to map Weather to. The @NamedQueries annotation defines a query which is used by the WeatherDAO in simple-persist. The query language in the @NamedQuery annotation is written in something called Hibernate Query Language (HQL). Each member variable is annotated with annotations that define the type of column and any relationships implied by that column:

id

The id property is annotated with @Id, this marks the id property as the property which contains the primary key in a database table. The @GeneratedValue controls how new primary key values are generated. In the case of id, we're using the IDENTITY GenerationType which will use the underlying database's identity generation facilities.

location

Each Weather object instance corresponds to a Location object. A Location object represents a zip code, and the @ManyToOne makes sure that Weather objects that point to the same Location object reference the same instance. The cascade attribute of the @ManyToOne makes sure that we persist a Location object every time we persist a Weather object.

condition, wind, atmosphere

Each of these objects is mapped as a @OneToOne with the CascadeType of ALL. This means that every time we save a Weather object, we'll be inserting a row into the Weather table, the Condition table, the Wind table, and the Atmosphere table.

date

Date is not annotated, this means that Hibernate is going to use all of the column defaults to define this mapping. The column name is going to be date and the column type is going to be the appropriate time to match the Date object. (Note: if you have a property you wish to omit from a table mapping, you would annotated that property with @Transient.)

Next, take a look at one of the secondary model objects, Condition. This class also resides in src/main/java/org/sonatype/mavenbook/weather/model.

Example 7.4. simple-model's Condition model object.

package org.sonatype.mavenbook.weather.model;

import javax.persistence.*;

@Entity
public class Condition {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;

    private String text;
    private String code;
    private String temp;
    private String date;

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="weather_id", nullable=false)
    private Weather weather;

    public Condition() {}

    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }

    // All getter and setter methods omitted...
}

The Condition class resembles the Weather class. It is annotated as an @Entity, and it has similar annotations on the id property. The text, code, temp, and date properties are all left with the default column settings, and the weather property is annotated with a @OneToOne annotation and another annotation that references the associated Weather object with a foreign key column named weather_id.

7.4. The Simple Weather Module

Favicon

The next module we're going to examine could be considered something of a "service". The Simple Weather module is the module which contains all of the logic necessary to retrieve and parse the data from the Yahoo! Weather RSS feed. While Simple Weather contains three Java classes and one JUnit test, it is going to present a single component, WeatherService, to both the Simple Web Application and the Simple Command-line Utility. Very often an enterprise project will contain several API modules which contain critical business logic or logic that interacts with external systems. A banking system might have a module that retrieves and parses data from a third-party data provider, and a system to display sports scores might interact with an XML feed that presents real-time scores for Basketball or Soccer. In this example, this module encapsulates all of the network activity and XML parsing which is involved in the interaction with Yahoo! Weather. Other modules can depend on this module, and simply call out to the retrieveForecast() method on WeatherService which takes a zip code as an argument and which returns a Weather object.

Example 7.5. simple-weather Module 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.ch07</groupId>
    <artifactId>simple-parent</artifactId>
    <version>1.0</version>
  </parent>
  <artifactId>simple-weather</artifactId>
  <packaging>jar</packaging>

  <name>Simple Weather API</name>

  <dependencies>
    <dependency>
      <groupId>org.sonatype.mavenbook.ch07</groupId>
      <artifactId>simple-model</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
    </dependency>
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.3.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>


The simple-weather POM extends the simple-parent POM, sets the packaging to jar and then adds the following dependencies:

org.sonatype.mavenbook.ch07:simple-model:1.0

simple-weather parses the Yahoo! Weather RSS feed into a Weather object. It has a direct dependency on simple-model.

log4j:log4j:1.2.14

simple-weather uses the Log4J library to print log messages.

dom4j:dom4j:1.6.1 and jaxen:jaxen:1.1.1

Both of these dependencies are used to parse the XML returned from Yahoo! Weather.

org.apache.commons:commons-io:1.3.2 (scope=test)

This test scoped dependency is used by the YahooParserTest.

Next is the WeatherService class, this class is going to look very similar to the WeatherService class from Example 6.3, “The WeatherService class”. While the WeatherService is the same, there are some subtle differences in this chapter's example. This version's retrieveForecast() method returns a Weather object and the formatting is going to be left to the applications which call WeatherService. The other major change is that the YahooRetriever and YahooParser are both bean properties of the WeatherService bean.

Example 7.6. The WeatherService class

package org.sonatype.mavenbook.weather;

import java.io.InputStream;

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

public class WeatherService {

  private YahooRetriever yahooRetriever;
  private YahooParser yahooParser;

  public WeatherService() {}

  public Weather retrieveForecast(String zip) throws Exception {
    // Retrieve Data
    InputStream dataIn = yahooRetriever.retrieve(zip);

    // Parse DataS
    Weather weather = yahooParser.parse(zip, dataIn);

    return weather;
  }

  public YahooRetriever getYahooRetriever() {
    return yahooRetriever;
  }

  public void setYahooRetriever(YahooRetriever yahooRetriever) {
    this.yahooRetriever = yahooRetriever;
  }

  public YahooParser getYahooParser() {
    return yahooParser;
  }

  public void setYahooParser(YahooParser yahooParser) {
    this.yahooParser = yahooParser;
  }
}

Finally, in this project, we have an XML file which is used by the Spring Framework to create something called an ApplicationContext. First, some explanation. Both of our applications, the web application and the command-line utility, need to interact with the WeatherService class, and they both do so by retrieving an instance of this class from a Spring ApplicationContext using the name weatherService. Our web application, uses a Spring MVC controller which is associated with an instance of WeatherService, and our command-line utility loads the WeatherService from an ApplicationContext in a static main() function. To encourage reuse, we've included an applicationContext-weather.xml file src/main/resources which is available on the classpath. Modules which depend upon the simple-weather module can load this application context using the ClasspathXmlApplicationContext in the Spring framework. They can then reference a named instance of the WeatherService named weatherService.

Example 7.7. Spring Application Context for the simple-weather Module

<?xml version="1.0" encoding="UTF-8"?>

<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="weatherService" 
             class="org.sonatype.mavenbook.weather.WeatherService">
      <property name="yahooRetriever" ref="yahooRetriever"/>
      <property name="yahooParser" ref="yahooParser"/>
    </bean>    

    <bean id="yahooRetriever" 
             class="org.sonatype.mavenbook.weather.YahooRetriever"/>    

    <bean id="yahooParser" 
             class="org.sonatype.mavenbook.weather.YahooParser"/>
</beans>

This document defines three beans: yahooParser, yahooRetriever, and weatherService. The weatherService bean is an instance of WeatherService, and this XML document populates the yahooParser and yahooRetriever properties with references to the named instances of the corresponding classes. Think of this applicationContext-weather.xml file as defining the architecture of a subsystem in this multi-module project. Projects like simple-webapp and simple-command can reference this context and retrieve an instance of WeatherService which already has relationships to instances of YahooRetriever and YahooParser.

7.5. The Simple Persist Module

Favicon

This module defines two very simple Data Access Objects (DAO). A DAO is a object which provides an interface for persistence operations. In an application which makes use of a 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 which allows us to retrieve a Location object by zip code. First, let's take a look at the simple-persist POM.

Example 7.8. 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.ch07</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.ch07</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>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-jta_1.1_spec</artifactId>
      <version>1.1</version>
    </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.ch07: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:javax 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 this non-free dependencies, we simple exclude this dependency from Hibernate and add a dependency on...

org.apache.geronimo.specs:geronimo-jta_1.1_spec:1.1

Just like the Servlet and JSP APIs, the Apache Geronimo project was nice enough to release a certified version of many of the enterprise APIs under an Apache License. This means that whenever a component tells you that it depends on the JDBC, JNDI, and JTA APIs (among others), you can look for a corresponding library under the org.apache.geronimo.specs groupId.

org.springframework:spring:2.0.7

This includes the entire Spring Framework as a dependency. (Note: It is generally a good practice to only depend upon the components of Spring you happen to be using. The Spring Framework project has been nice enough to create focused artifacts like spring-hibernate3.)

Why depend on Spring? When it comes to Hibernate integration, Spring allows us to leverage helper classes like HibernateDaoSupport. For an example of what is possible with the help of HibernateDaoSupport, take a look at the code for the WeatherDAO below.

Example 7.9. 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 {

    public WeatherDAO() {}

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

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

    @SuppressWarnings("unchecked")
    public List<Weather> recentForLocation( final Location location ) {
      return (List<Weather>) getHibernateTemplate().execute(
        new HibernateCallback() {
        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.

It is 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 following XML document is stored in src/main/resources in a file named applicationContext-persist.xml.

Example 7.10. 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 like 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 see how we can use the Maven Hibernate3 plugin to generate our database schema from the annotated model objects. For this to work properly, the Maven Hibernate3 plugin needs to read the JDBC connection configuration parameters, the list of annotated classes, and other Hibernate configuration from a file named 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 DDL from nothing more than our annotations.

Example 7.11. 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 Example 7.10, “Spring Application Context for simple-persist” and Example 7.1, “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.

7.6. The Simple Web Application Module

Favicon

The web application is defined in a simple-webapp project. This simple web application project is going to define two Spring MVC Controllers: WeatherController and HistoryController. Both of these controllers are going to reference components defined in the the simple-weather and simple-persist. The Spring container is configured in this application's web.xml which references the applicationContext-weather.xml file in simple-weather and the applicationContext-persist.xml file in simple-persist. The component architecture of this simple web application is shown in Figure 7.3, “Spring MVC Controllers Referencing Components in simple-weather and simple-persist.”.

Spring MVC Controllers Referencing Components in simple-weather and simple-persist.

Figure 7.3. Spring MVC Controllers Referencing Components in simple-weather and simple-persist.


The POM for simple-webapp is shown below.

Example 7.12. POM for simple-webapp

<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.ch07</groupId>
    <artifactId>simple-parent</artifactId>
    <version>1.0</version>
  </parent>

  <artifactId>simple-webapp</artifactId>
  <packaging>war</packaging>
  <name>Simple Web Application</name>
  <dependencies>
    <dependency> 
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-servlet_2.4_spec</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>org.sonatype.mavenbook.ch07</groupId>
      <artifactId>simple-weather</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.sonatype.mavenbook.ch07</groupId>
      <artifactId>simple-persist</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring</artifactId>
      <version>2.0.7</version>
    </dependency>
    <dependency>
      <groupId>org.apache.velocity</groupId>
      <artifactId>velocity</artifactId>
      <version>1.5</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>simple-webapp</finalName>
    <plugins>
      <plugin> 
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
        <dependencies>
          <dependency>
            <groupId>hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <version>1.8.0.7</version>
          </dependency>
        </dependencies>        
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId> 
        <artifactId>hibernate3-maven-plugin</artifactId>
        <version>2.0</version>
        <configuration>
          <components>
            <component>
              <name>hbm2ddl</name>
              <implementation>annotationconfiguration</implementation> 
            </component>
          </components>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <version>1.8.0.7</version>
          </dependency>
        </dependencies>        
      </plugin>
    </plugins>
  </build>
</project>

As this book progresses and the examples become more and more substantial, you'll notice that the pom.xml begins to take on some weight. In this POM, we're configuring four dependencies and configuring two plugins, let's go through this POM in detail and dwell on some of the important configuration points:

1

This simple-webapp project defines four dependencies: the Servlet 2.4 specification implementation from Apache Geronimo, the simple-weather service library, the simple-persist persistence library, and the entire Spring Framework 2.0.7.

2

The Maven Jetty Plugin couldn't be easier to add to this project, we simply add a plugin element that references the appropriate groupId and artifactId. The fact that this plugin is so trivial to configure means that the plugin developers did a good job of provide adequate defaults that don't need to be overridden in most cases. If you did need to override the configuration of Jetty plugin you would do so by providing a configuration element.

3

In our build configuration, we're going to be configuring the Maven Hibernate3 Plugin to hit an embedded HSQLDB instance. For the Maven Hibernate 3 plugin to successfully connect to this database using JDBC, the plugin will need reference the HSQLDB JDBC driver on the classpath. To make a dependency available for a plugin, we add a dependency declaration right inside plugin declaration. In this case, we're referencing hsqldb:hsqldb:1.8.0.7. The Hibernate plugin also needs the JDBC driver to create the database, so we have also added this dependency to its configuration.

4

The Maven Hibernate plugin is when this POM starts to get interesting. In the next section, we're going to run the hbm2ddl goal to generate a HSQLDB database. In this pom.xml, we're including a reference to version 2.0 of the hibernate3-maven-plugin hosted by the Codehaus Mojo plugin.

5 <