More Books
Hibernate: A J2EE Developer's Guide
Hibernate: A J2EE™ Developer's Guide
Table of Contents
Copyright
Acknowledgments
About the Author
Preface
Required Skills
Roadmap
Chapter 1. Overview
Why Object/Relational Mapping?
What Is Hibernate?
Comparing JDBC to Hibernate
Hibernate's Mapping System
Other Java/Database Integration Solutions
How to Obtain and Install
Supported Databases
Chapter 2. Getting Oriented
Application Architecture
Mapping Files
Generating Java Source
Application Configuration
Web Application
JSP Interface
Chapter 3. Starting from Java
Java Object Model
Generated Mapping Files
Generated Schema
Working with Artifacts and Owners
Chapter 4. Starting from an Existing Schema
Initial Schema
Using Middlegen
Generated Mapping Files
Generated Java
Working with the Database
Chapter 5. Mapping Files
Basic Structure
Mapping File Reference
Chapter 6. Persistent Objects
Sessions
Objects and Identity
Life-Cycle Methods
Chapter 7. Relationships
Database Relationships
Java Collection Relationships
Java Class Relationships
Any-Based Relationships
Bi-directional Relationships
Chapter 8. Queries
HQL
HQL Reference
Select
From
Where
Group By
Having
Order By
Criteria Queries
Native SQL Queries
Chapter 9. Transactions
Introduction to Transactions
Optimistic and Pessimistic Locking
Chapter 10. Performance
Finding and Solving Problems
Queries
Inserts
Connection Pooling
Caching
Chapter 11. Schema Management
Updating an Existing Schema
Generating Update and Drop Scripts
Chapter 12. Best Practices, Style Guide, Tips and Tricks
Reducing Code with Inversion of Control
Reducing Session Creation Impact with ThreadLocal
Using Hibernate as an EJB BMP Solution
Integrating with Other Technologies
Applications That Use Hibernate
Strategies for Getting Started
Chapter 13. Future Directions
Hibernate 3.0
EJB 3.0
Here and Now
Index
SYMBOL
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X

Java Object Model

This example application models a set of historical artifacts owned by a complex set of owners, including museums, foundations, exhibits, and individual owners. A given artifact may be owned by none, one, or many of these possible owners. Thus the example illustrates how two powerful concepts are used in Hibernate: the use of the Java collection framework to manage the ownership of artifacts by various owners, and the use of a class hierarchy to describe the various kinds of owners.

As shown in Figure 3.1, the application defines an abstract Owner class, with four possible concrete implementations. Some of these subclasses in turn may have an owner.

Figure 3.1. Ownership Object Model


One or more of these possible owners may be connected to a particular Artifact, as shown in Figure 3.2.

Figure 3.2. Artifact Class


Therefore, a given Artifact may be owned by zero or more owners of many different types. For example, a single artifact may be owned by a specific person, be on permanent loan to a museum, be managed by a foundation, and be part of two different exhibitions. This is a complex set of relationships, but not an atypical model, especially for an object-oriented developer.

Java Classes

The Java classes in this application contain several XDoclet tags. Before reviewing the Java classes in this application, a brief introduction to @ tags is required.

The @ symbol is generally used to provide an additional bit of information about a class or method for a source parser such as javadoc or XDoclet. The @hibernate. text is used to preface XDoclet Hibernate tags; all the other @ markup tags in this application are used by javadoc.

The @hibernate tags shown in Figure 3.3 are used in this application. A full list of tags can be found at http://xdoclet.sourceforge.net/tags/hibernate-tags.html. These tags correspond to the relevant HBM tags, as described in more detail in Chapter 5.

Let's start by looking at the abstract Owner class, as shown in Listing 3.1. It is superficially an ordinary Java class, but pay close attention to the various @ comment values. Note the convenient Owner.addArtifact() methodsnot necessary, but helpful.

Listing 3.1. Owner Class
package com.cascadetg.ch03;

import java.io.Serializable;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

/**
 * @author Will Iverson
 * @hibernate.class
 * @hibernate.discriminator column="discriminator"
 * @since 1.0
 */
public abstract class Owner implements Serializable
{
    long id;
    String name;
    java.util.Set artifacts = new java.util.HashSet();

    /**
     * @hibernate.property
     * @return Returns the name.
     */
    public String getName() { return name; }

    /**
     * @param name The name to set.
     */
    public void setName(String name) { this.name = name; }

    /**
      * @hibernate.set table="ownership" inverse="false"
        lazy="true"
      * @hibernate.collection-key column="owner_id"
      * @hibernate.collection-many-to-many column="artifact_id"
        class="com.cascadetg.ch03.Artifact"
      * @return Returns the owners.
      */

    public java.util.Set getArtifacts() { return artifacts; }

    /**
     * @param owners
     * The owners to set.
     */
    public void setArtifacts(java.util.Set artifacts)
    { this.artifacts = artifacts; }

    public void addArtifact(Artifact newArtifact, boolean
         already)
    {
        artifacts.add(newArtifact);
        if(!already)
            newArtifact.addOwner(this, true);
    }

    /** Utility function to make adding an artifact easier */
    public void addArtifact(Artifact newArtifact)
    { this.addArtifact(newArtifact, true); }

    /** Standard Hibernate equality override */
    public boolean equals(Object other)
    {
        try
        {
            Owner castOther = (Owner)other;
            return new EqualsBuilder()
                .append(this.getId(), castOther.getId())
                .isEquals();
        } catch (Exception e) { return false; }
    }

    /** Standard Hibernate hash override */
    public int hashCode()
    {
        return new
        HashCodeBuilder().append(getId()).toHashCode();
    }

    /**
     * @hibernate.id generator-class="native"
     * @return Returns the id.
     */
    public long getId() { return id; }

    /**
     * @param id The id to set.
     */
    public void setId(long id) { this.id = id; }

}

Table 3.1. Reference XDoclet Tags

@hibernate.class

Indicates that a *.hbm.xml file should be generated for this class.

@hibernate.subclass

Indicates that the information for this class should be generated using the subclass tag. For more information on the subclass tag, see Chapter 5.

@hibernate.discriminator

Because the application maps multiple classes to a single table, a column is needed in the table for Hibernate to store the class type. For more information, see the discriminator tag in Chapter 5.

@hibernate.property

Indicates to XDoclet that this get/set pair should be treated as a persistent property. If you don't use this flag, XDoclet won't generate a persistent property mapping in the *.hbm.xml file.

@hibernate.set

This application uses a java.util.Set to store the association between zero and one or more owners or between zero and one or more artifacts. Hibernate needs to know the table to use as an association table. This tag identifies the table. For descriptions of the inverse="false" and lazy="true" values, see the set tag in Chapter 5, and relationships in Chapter 7.

@hibernate.collection-key

This is used to store the id of the referenced artifact.


Given the base Owner class, the first subclass, Exhibition, is shown in Listing 3.2. This class defines several additional properties over the base Owner class.

Listing 3.2. Exhibition Class
package com.cascadetg.ch03;

/**
 * @author Will Iverson
 * @hibernate.subclass
 * @since 1.0
 */

public class Exhibition extends Owner
{

    Owner owner;
    String location;
    java.util.Date startDate;
    java.util.Date endDate;

    /**
     * @hibernate.property
     * @return Returns the endDate.
     */
    public java.util.Date getEndDate() { return endDate; }

    /**
     * @param endDate The endDate to set.
     */
    public void setEndDate(java.util.Date endDate)
        { this.endDate = endDate; }

    /**
     * @hibernate.property
     * @return Returns the location.
     */
    public String getLocation() { return location; }

    /**
     * @param location The location to set.
     */
    public void setLocation(String location)
       { this.location = location; }

    /**
     * @hibernate.many-to-one lazy="true"
     * @return Returns the owner.
     */
    public Owner getOwner() { return owner; }

    /**
     * @param owner The owner to set.
     */
    public void setOwner(Owner owner) { this.owner = owner; }

    /**
     * @hibernate.property
     * @return Returns the startDate.
     */
    public java.util.Date getStartDate() { return startDate; }

    /**
     * @param startDate The startDate to set.
     */
    public void setStartDate(java.util.Date startDate)
    { this.startDate = startDate; }
}

Listing 3.3 shows the next subclass, Foundation. This class defines several additional properties over the base Owner class. Some are similar to those described by Foundation, others are different.

Listing 3.3. Foundation Class
package com.cascadetg.ch03;

/**
 * @author Will Iverson
 * @hibernate.subclass
 * @since 1.0
 */

public class Foundation extends Owner
{
    Owner owner;
    String location;
    java.util.Date dateCreated;

    /**
     * @hibernate.property
     * @return Returns the dateCreated.
     */
    public java.util.Date getDateCreated()
    { return dateCreated; }

    /**
     * @param dateCreated The dateCreated to set.
     */
    public void setDateCreated(java.util.Date dateCreated)
    { this.dateCreated = dateCreated; }

    /**
     * @hibernate.property
     * @return Returns the location.
     */
    public String getLocation()
    { return location; }

    /**
     * @param location The location to set.
     */
    public void setLocation(String location)
    { this.location = location; }

    /**
     * @hibernate.many-to-one
     * @return Returns the owner.
     */
    public Owner getOwner() { return owner; }

    /**
     * @param owner The owner to set.
     */
    public void setOwner(Owner owner) { this.owner = owner; }
}

Listing 3.4 shows the next subclass, Museum. Again, this class defines several additional properties over the base Owner class. Some are similar to those described by other subclasses, others are different.

Listing 3.4. Museum Class
package com.cascadetg.ch03;

/**
 * @author Will Iverson
 * @hibernate.subclass
 * @since 1.0
 */

public class Museum extends Owner
{

    String location;
    java.util.Date dateOpened;

    /**
     * @hibernate.property
     * @return Returns the dateOpened.
     */
    public java.util.Date getDateOpened() { return dateOpened; }

    /**
     * @param dateOpened The dateOpened to set.
     */
    public void setDateOpened(java.util.Date dateOpened)
    { this.dateOpened = dateOpened; }

    /**
     * @hibernate.property
     * @return Returns the location.
     */
    public String getLocation() { return location; }

    /**
     * @param location The location to set.
     */
    public void setLocation(String location)
    { this.location = location; }

}

Listing 3.5 shows the final subclass, Person. This class also defines several additional properties over the base Owner class.

Listing 3.5. Person Class
package com.cascadetg.ch03;

import java.util.Date;

/**
 * @author Will Iverson
 * @hibernate.subclass
 * @since 1.0
 */

public class Person extends Owner
{
    Date birthDate;
    Date deathDate;

    /**
     * @hibernate.property
     * @return Returns the birthDate.
     */
    public Date getBirthDate() { return birthDate; }

    /**
     * @param birthDate The birthDate to set.
     */
    public void setBirthDate(Date birthDate)
    { this.birthDate = birthDate; }

    /**
     * @hibernate.property
     * @return Returns the deathDate.
     */
    public Date getDeathDate() { return deathDate; }

    /**
     * @param deathDate The deathDate to set.
     */
    public void setDeathDate(Date deathDate)
        { this.deathDate = deathDate; }

}

Working with XDoclet

With Java files properly marked up, XDoclet (in conjunction with Ant, described in Chapter 2) can be used to generate the *.hbm.xml mapping files needed by Hibernate.

WHAT ABOUT MAPGENERATOR?

Hibernate includes a tool for generating HBM files from Java class files called MapGenerator. MapGenerator, unfortunately, does not generate complete *.hbm.xml information (but instead makes an incomplete best guess). This means that as you edit and modify your Java class files, you'll need to rerun the MapGenerator and then merge changes back into your existing *.hbm.xml file by hand. Using XDoclet lets you generate your *.hbm.xml files from your Java source, thereby eliminating the need for hand-merging.


XDoclet can be downloaded from http://xdoclet.sourceforge.net/ (this text was written using xdoclet-bin-1.2). Once XDoclet has been downloaded, simply unzip or untar it into a directory you can easily find. You will then need to modify the xdoclet_lib_path, as shown in Listing 3.6, to point to the lib directory of the downloaded XDoclet.

The build file shown in Listing 3.6 compiles the Java source and then executes the XDoclet task to generate the *.hbm.xml files automatically. Pay close attention to the configuration of the XDoclet task.

Listing 3.6. XDoclet Build File
<?xml version="1.0"?>
<project name="ch03" default="all">

      <target name="all" depends="compile,build_hbm" />

      <description>Hibernate starting with Java</description>

      <!-- You'll need to set these depending on your library
         installation directories -->
      <property name="hibernate_path"
            value="C:\devenv\hibernate-2.1.2"/>
      <property name="hibernate_tool_path"
            value="C:\devenv\hibernate-extensions-2.1\tools"/>
      <property name="xdoclet_lib_path"
            value="C:\devenv\xdoclet-bin-1.2\lib"/>

      <!-- Normally, build files are stored at the root of the
           tree.
         The builds for this book are on a per-chapter basis -->
      <property name="base_dir" value="..\..\..\" />

      <!-- This defines the Hibernate XDoclet task.  You can
         copy this verbatium, assuming you set the xdoclet
         lib path above -->
      <taskdef name="hibernatedoclet"
         classname="xdoclet.modules.hibernate.Hibernate
         DocletTask">
            <classpath>
                <fileset dir="${xdoclet_lib_path}">
                    <include name="*.jar"/>
                </fileset>
            </classpath>
      </taskdef>

    <path id="project.class.path">
        <pathelement
            location="${hibernate_path}\hibernate2.jar"/>
        <pathelement
            location="${hibernate_tool_path}\hibernate-
            tools.jar"/>
        <pathelement location=
            "${hibernate_path}\lib\commons-collections-2.1.jar"
            />

        <pathelement location=
            "${hibernate_path}\lib\commons-logging-1.0.3.jar"
            />
        <pathelement location=
            "${hibernate_path}\lib\commons-lang-1.0.1.jar" />
        <pathelement location=
            "${hibernate_path}\lib\xerces-2.4.0.jar" />
        <pathelement location=
            "${hibernate_tool_path}\lib\jdom.jar"/>
    </path>

 <!-- Compiles the code using javac.  If the files won't
      compile, you won't be able to generate HBM files. -->
 <target name="compile">
      <javac srcdir="${base_dir}" destdir="${base_dir}">
            <include name="**\ch03\*.java" />
                        <classpath refid="project.class.path"/>
      </javac>
 </target>


<!--   The force attribute causes the system to regenerate the
       files every time the task is run.

       Merge dir can be used to specify text that should be
       automatically included with the generated HBM files.

       If you don't specify the version as 2.0, you will get
       out-of-date v1.x HBM files -->
      <target name="build_hbm"
            description="builds_hbm_file" depends="compile">
            <hibernatedoclet
                destdir="${base_dir}"
                excludedtags="@version,@author,@todo"
                force="true"
                mergedir="${base_dir}"
                verbose="true">
                    <fileset dir="${base_dir}">
                            <include name="**\ch03\*.java" />
                    </fileset>
                    <hibernate version="2.0"/>
            </hibernatedoclet>
      </target>
</project>