
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.
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.
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”.
In Figure 7.1, “Multi-module Enterprise Application Module Relationships”, you can see that there are five submodules of simple-parent, they are:
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.
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.
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.
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.
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.
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/.
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.
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.
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:
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.
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.
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 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.
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:
simple-weather parses the Yahoo! Weather
RSS feed into a Weather
object. It has a direct dependency on
simple-model.
simple-weather uses the Log4J library to
print log messages.
Both of these dependencies are used to parse the XML returned from Yahoo! Weather.
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.
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:
Just like the simple-weather module, this
persistence module references the core model objects defined in
simple-model
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...
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.
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:
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:
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.
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.
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.”.
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: