Maven by Example

7.3. The Simple Model Module

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.

figs/web/multimodule-web-spring_object-model.png

Figure 7.2. Simple Object Model for Weather Data


The pom.xml file for this simple model object contains one dependency that 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 shown in simple-model pom.xml, and then look at the next few examples for some illustrations of these annotations.

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.multispring</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 <phrase>setter named <methodname role="keep-together">setName(String name). Although 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. See Annotated Weather Model Object.

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. Although a full explanation of Hibernate annotations is beyond the scope of this chapter, here is a 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 that 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 that 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 annotate that property with @Transient.

Next, take a look at one of the secondary model objects, Condition, shown in simple-model’s Condition Model Object.. This class also resides in src/main/java/org/sonatype/mavenbook/weather/model.

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.