Chapter 3. Starting from Java

Combining Hibernate with XDoclet makes it possible to start from Java code when building an application. Using XDoclet markup tags (similar to the tags used by javadoc), you can generate the *.hbm.xml files required by Hibernate directly from your Java source files.

XDoclet, available from http://xdoclet.sourceforge.net/, is an open-source framework for annotating Java source files with special comments, which are then used to construct configuration files used by a variety of different environments.

Many developers first encounter XDoclet when working with EJB 2.X as a tool to help manage the complexity of EJB 2.X development. XDoclet-style annotations have become so popular that support for similar annotations (as described by JSR 175, http://jcp.org/en/jsr/detail?id=175) is included as a standard extension in the upcoming release of Java (Java 2 Platform, Standard Edition 5.0). The early draft specification for EJB 3.0 uses annotations in lieu of XML descriptors. The upshot is that the use of XDoclet and Hibernate is (as of this writing) a close approximation of the development model being suggested for EJB 3.0.

WARNING

There are three significant downsides to using annotations as your development solution. The first is the addition of (yet another) complex tool to your build process. The second is the relative difficulty of debugging and otherwise tracing the source of problems with the generated mapping file. The third downside of annotations is that configuration information is now stored directly in your Java source files. Given all of these downsides, I have found annotations to be much more error-prone and difficult to work with than simply starting from a mapping file, as described in Chapter 2.

In constructing the application in this chapter, we will start from a set of Java sources and then walk through the rest of the development path.

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.

Ownership Object Model

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.

Artifact Class

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() methods—not necessary, but helpful.

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

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

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

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

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

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.

Example 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:devenvhibernate-2.1.2"/>
      <property name="hibernate_tool_path"
            value="C:devenvhibernate-extensions-2.1	ools"/>
      <property name="xdoclet_lib_path"
            value="C:devenvxdoclet-bin-1.2lib"/>

      <!-- 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}libcommons-collections-2.1.jar"
            />

        <pathelement location=
            "${hibernate_path}libcommons-logging-1.0.3.jar"
            />
        <pathelement location=
            "${hibernate_path}libcommons-lang-1.0.1.jar" />
        <pathelement location=
            "${hibernate_path}libxerces-2.4.0.jar" />
        <pathelement location=
            "${hibernate_tool_path}libjdom.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>

Generated Mapping Files

The XDoclet tags generate mapping files for you automatically. Despite our five classes, XDoclet only generates two mapping documents and stores the subclasses in the same document as the base class. Therefore, the application only has an Owner.hbm.xml file and an Artifact.hbm.xml.

Listing 3.7 shows the Artifact mapping file, with some of the extraneous white space removed. For additional details on the various attributes and elements, see Chapter 5.

Example 3.7. Artifact Mapping File

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>
    <class name="com.cascadetg.ch03.Artifact"
        dynamic-update="false"
        dynamic-insert="false">

        <id name="id" column="id" type="long">
            <generator class="native" />
        </id>

        <property name="comments" type="java.lang.String"
            update="true" insert="true" column="comments"/>

        <property name="dateCreated" type="java.util.Date"
            update="true" insert="true" column="dateCreated" />

        <property name="dateDiscovered" type="java.util.Date"
            update="true" insert="true" column="dateDiscovered"
            />

        <property name="description" type="java.lang.String"
            update="true" insert="true" column="description" />

        <property name="location" type="java.lang.String"
            update="true" insert="true" column="location" />

        <set name="owners" table="ownership" lazy="false"
            inverse="true" cascade="none" sort="unsorted">
              <key column="artifact_id" />
              <many-to-many class="com.cascadetg.ch03.Owner"
                  column="owner_id" outer-join="auto" />
        </set>

        <property name="title" type="java.lang.String"
            update="true" insert="true" column="title" />

        <!--
        To add non XDoclet property mappings, create a
        file named hibernate-properties-Artifact.xml
        containing the additional properties and place
        it in your merge dir.
        -->
    </class>
</hibernate-mapping>

The mapping file for Owner and the subclasses, as shown in Listing 3.8, is significantly more complex than the one for Artifact. In addition to the properties of the Owner class, each subclass is listed with the properties unique to that class. You are more or less allowed to build hierarchies of arbitrary complexity—subclasses of subclasses are allowed, for example. As may be imagined, you can build very complex hierarchies.

Example 3.8. Owner (and Subclasses) Mapping File

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-
    2.0.dtd">

<hibernate-mapping>
    <class name="com.cascadetg.ch03.Owner"
        dynamic-update="false" dynamic-insert="false" >

        <id name="id" column="id" type="long">
            <generator class="native" />
        </id>
        <discriminator column="discriminator" type="string" />

        <property name="name" type="java.lang.String"
            update="true" insert="true" column="name" />

        <set name="artifacts" table="ownership" lazy="true"
            inverse="false" cascade="none" sort="unsorted">
              <key column="owner_id" />

              <many-to-many class="com.cascadetg.ch03.Artifact"
                  column="artifact_id" outer-join="auto" />
        </set>

        <!--
            To add non XDoclet property mappings, create a file
            named
            hibernate-properties-Owner.xml
            containing the additional properties
            and place it in your merge dir.
        -->
        <subclass name="com.cascadetg.ch03.Foundation"
            dynamic-update="false" dynamic-insert="false" >

        <property name="dateCreated" type="java.util.Date"
            update="true" insert="true" column="dateCreated" />

        <property name="location" type="java.lang.String"
            update="true" insert="true" column="location" />

        <many-to-one name="owner"
        class="com.cascadetg.ch03.Owner"
            cascade="none" outer-join="auto" update="true"
            insert="true" column="owner" />
          <!--
                  To add non XDoclet property mappings,
                  create a file named
                  hibernate-properties-Foundation.xml
                  containing the additional properties and
                  place it in your merge dir.
          -->
        </subclass>
        <subclass name="com.cascadetg.ch03.Exhibition"
            dynamic-update="false" dynamic-insert="false" >

        <property name="endDate" type="java.util.Date"
            update="true" insert="true" column="endDate" />

        <property name="location" type="java.lang.String"
            update="true" insert="true" column="location" />

        <many-to-one name="owner"
        class="com.cascadetg.ch03.Owner"
            cascade="none" outer-join="auto" update="true"
            insert="true" column="owner" />

        <property name="startDate" type="java.util.Date"
            update="true" insert="true" column="startDate" />
        <!--
            To add non XDoclet property mappings, create a file
            named hibernate-properties-Exhibition.xml
            containing the additional properties and
            place it in your merge dir.
        -->
        </subclass>
        <subclass name="com.cascadetg.ch03.Museum"
            dynamic-update="false" dynamic-insert="false" >

        <property name="dateOpened" type="java.util.Date"
            update="true" insert="true" column="dateOpened" />

        <property name="location" type="java.lang.String"
            update="true" insert="true" column="location" />
        <!--
            To add non XDoclet property mappings, create a file
            named hibernate-properties-Museum.xml
            containing the additional properties and
            place it in your merge dir.
        -->
        </subclass>
        <subclass name="com.cascadetg.ch03.Person"
            dynamic-update="false" dynamic-insert="false" >

        <property name="birthDate" type="java.util.Date"
            update="true" insert="true" column="birthDate" />

        <property name="deathDate" type="java.util.Date"
            update="true" insert="true" column="deathDate" />
            <!--
            To add non XDoclet property mappings, create a file
            named hibernate-properties-Person.xml
            containing the additional properties and
            place it in your merge dir.
        -->
        </subclass>
    </class>
</hibernate-mapping>

Generated Schema

The application in this chapter is intended to generate the database schema from the mapping files, generated in turn from the Java source. The representation of the Artifact object in a database is fairly straightforward, as shown in Listing 3.9.

Example 3.9. Artifact Table Definition

mysql> desc artifact;
+----------------+--------------+------+-----+---------+------+
| Field          | Type         | Null | Key | Default | Extra
|
+----------------+--------------+------+-----+---------+------+
| id             | bigint(20)   |      | PRI | NULL    |
                                               auto_increment |
| comments       | varchar(255) | YES  |     | NULL    |      |
| dateCreated    | datetime     | YES  |     | NULL    |      |
| dateDiscovered | datetime     | YES  |     | NULL    |      |
| description    | varchar(255) | YES  |     | NULL    |      |
| location       | varchar(255) | YES  |     | NULL    |      |
| title          | varchar(255) | YES  |     | NULL    |      |
+----------------+--------------+------+-----+---------+------+
7 rows in set (0.00 sec)

The representation of the Owner class and the related subclasses, however, is a bit more complex, as shown in Listing 3.10.

Example 3.10. Owner Table Definition

mysql> desc owner;
+---------------+--------------+------+-----+---------+-------+
| Field         | Type         | Null | Key | Default | Extra
+---------------+--------------+------+-----+---------+-------+
| id            | bigint(20)   |      | PRI | NULL    | auto_                             
Owner Table Definition                        increment |
| discriminator | varchar(255) |      |     |         |        |
| name          | varchar(255) | YES  |     | NULL    |        |
| dateCreated   | datetime     | YES  |     | NULL    |        |
| location      | varchar(255) | YES  |     | NULL    |        |
| owner         | bigint(20)   | YES  | MUL | NULL    |        |
| endDate       | datetime     | YES  |     | NULL    |        |
| startDate     | datetime     | YES  |     | NULL    |        |
| dateOpened    | datetime     | YES  |     | NULL    |        |
| birthDate     | datetime     | YES  |     | NULL    |        |
| deathDate     | datetime     | YES  |     | NULL    |        |
+---------------+--------------+------+-----+---------+-------+

Looking at the owner table in Listing 3.10 more closely, several interesting details emerge. The discriminator column is used to store the class type information regarding the record. Listing 3.11 shows an example of several records stored in the owner table; note the class information.

Example 3.11. Sample Owner Records

mysql> select id, discriminator, name from owner;
+----+-------------------------------+-------------------------+
| id | discriminator                 | name                    |
+----+-------------------------------+-------------------------+
|  1 | com.cascadetg.ch03.Person     | Smith, Bob              |
|  2 | com.cascadetg.ch03.Foundation | Smith Foundation        |
|  3 | com.cascadetg.ch03.Museum     | Waldendorf              |
|  4 | com.cascadetg.ch03.Exhibition | New Acquisitions 2005   |
+----+-------------------------------+-------------------------+
4 rows in set (0.00 sec)

Any column not relevant to a particular subclass is set to null. For example, a Person has no date-opened property, and therefore the dateOpened column for Person records is set to null.

Hibernate automatically manages the discriminator for you as you work with persistent objects. For example, when Hibernate retrieves the four records shown in Listing 3.11, it will automatically instantiate the proper object and set the values for you, returning a set of Owner objects with the proper underlying class.

In addition to the expected two tables, Hibernate also uses a third table to manage the relationships between artifacts and their owners, as shown in Listing 3.12. This table isn't mapped to a particular Java class; it is instead implied by the bi-directional set references on both Owner and Artifact. In database terminology, this is a many-to-many relationship. For more information on the link between a set collection and a many-to-many database relationship, see Chapter 7.

Example 3.12. Ownership Table

mysql> desc ownership;
+-------------+------------+------+-----+---------+------------+
| Field       | Type       | Null | Key | Default | Extra      |
+-------------+------------+------+-----+---------+------------+
| owner_id    | bigint(20) |      | PRI | 0       |            |
| artifact_id | bigint(20) |      | PRI | 0       |            |
+-------------+------------+------+-----+---------+------------+
2 rows in set (0.00 sec)

Working with Artifacts and Owners

This final section of the chapter provides a sample set of operations. The entire class is shown in Listing 3.13, broken up to allow for commentary.

The code shown in Listing 3.13 shows the primary flow for the test application. A SessionFactory is shared throughout the execution of the application. An array of strings is used to describe a set of basic objects. The main() method tests the creation, update, and deletion of a set of objects.

Example 3.13. Setting Up the Code

package com.cascadetg.ch03;

/** Note that we leverage a JDBC connection for
 * our mass delete code.
 */
import java.sql.Connection;
import java.sql.Statement;

/** Various Hibernate-related imports */
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.SchemaUpdate;
import net.sf.hibernate.expression.Order;

public class TestSuite
{
    /** We use this session factory to create our sessions */
    public static SessionFactory sessionFactory;

    /** Our artifact data */
    public static String[] artifactNames =
        {
            "Bast Figurine, Faience",
            "Obsidian Spearpoint, Maglenian Period",
            "Lion Bowl, Unidentified",
            "Golden Torq, Six Inch Diameter",
            "Silver Ring, Roman" };

    static long update_id;

    public static void main(String[] args)
    {
        initialization();
        if (currentCount() == 0)
            System.out.println("initialization OK");

        createObjects();
        if (currentCount() == 5)
            System.out.println("createObjects OK");

        loadAllArtifacts();
        if (currentCount() == 5)
            System.out.println("loadAllArtifacts OK");

        updateOneObject();
        if (currentCount() == 5)
            System.out.println("updateOneObject OK");

        deleteObjects();
        if (currentCount() == 4)
            System.out.println("deleteObjects OK");

        massDelete();
        if (currentCount() == 0)
            System.out.println("massDelete OK");
    }

Listing 3.14 shows the use of HQL to determine the number of artifacts currently in the system. For more information on HQL, see Chapter 8.

Example 3.14. Obtaining the Current Count

    public static int currentCount()
    {
        System.out.println();
        Session hibernateSession = null;
        Transaction myTransaction = null;
        Integer result = null;
        try
        {
            hibernateSession = sessionFactory.openSession();
            myTransaction =
                 hibernateSession.beginTransaction();

            String hql =
            "select count(artifact) from Artifact as artifact";

            Query myQuery = hibernateSession.createQuery(hql);

            result = (Integer)myQuery.uniqueResult();
            System.out.println(
                "Current count: " + result.toString());

            myTransaction.commit();

        } catch (Exception e)
        {
            e.printStackTrace();
            try
            {
                myTransaction.rollback();
            } catch (Exception e2)
            {
                // Silent failure of transaction rollback
            }
        } finally
        {
            try
            {
                if (hibernateSession != null)
                    hibernateSession.close();
            } catch (Exception e)
            {
                // Silent failure of session close
            }
        }
        return result.intValue();
    }

Listing 3.15 shows the standard initialization of a Hibernate application. Note the use of the SchemaUpdate class to ensure that the database schema is brought in line with the current mapping files (see Chapter 11 for more information on schema management).

Example 3.15. Setting Up the Code

    /** Loads the Hibernate configuration information,
     * sets up the database and the Hibernate session factory.
     */
    public static void initialization()
    {
        //System.setErr(System.out);
        System.out.println("initialization");
        try
        {
            Configuration myConfiguration = new
                 Configuration();

            myConfiguration.addClass(Owner.class);
            myConfiguration.addClass(Artifact.class);

            // This is the code that updates the database to
            // the current schema.
            new SchemaUpdate(myConfiguration).execute(true,
                true);

            // Sets up the session factory (used in the rest
            // of the application).
            sessionFactory =
                 myConfiguration.buildSessionFactory();

        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }

Listing 3.16 shows the creation of a set of objects, along with the establishment of the relationships between various objects. Note that nowhere is the ownership table referenced by name; the relationships are instead managed by Hibernate. For more information on the management of relationships by Hibernate, see Chapter 7.

Example 3.16. Creating Objects

    public static void createObjects()
    {
        System.out.println();
        System.out.println("createObjects");

        Session hibernateSession = null;
        Transaction myTransaction = null;
        try
        {
            hibernateSession = sessionFactory.openSession();
            myTransaction =
                 hibernateSession.beginTransaction();

            Owner myExhibition = new Exhibition();
            myExhibition.setName("New Acquisitions 2005");

            Person myPerson = new Person();
            myPerson.setName("Smith, Bob");

            Foundation myFoundation = new Foundation();
            myFoundation.setName("Smith Foundation");
            myFoundation.setOwner(myPerson);

            Museum myMuseum = new Museum();
            myMuseum.setName("Waldendorf");
            myMuseum.setDateOpened(new java.util.Date());

            hibernateSession.save(myPerson);
            hibernateSession.save(myFoundation);
            hibernateSession.save(myMuseum);
            hibernateSession.flush();
            myTransaction.commit();

            myTransaction = hibernateSession.beginTransaction
                 ();

            for (int i = 0; i < artifactNames.length; i++)
            {
                Artifact myArtifact = new Artifact();
                myArtifact.setTitle(artifactNames[i]);
                hibernateSession.save(myArtifact);

                // Important to issue the save first,
                // so the ID of the artifact is set.
                myArtifact.addOwner(myExhibition);
                myArtifact.addOwner(myFoundation);
                myArtifact.addOwner(myMuseum);

                hibernateSession.save(myExhibition);
                hibernateSession.save(myFoundation);
                hibernateSession.save(myMuseum);

                hibernateSession.flush();
            }

            myTransaction.commit();
        } catch (Exception e)
        {
            e.printStackTrace();
            try
            {
                myTransaction.rollback();
            } catch (Exception e2)
            {
                // Silent failure of transaction rollback
            }
        } finally
        {
            try
            {
                hibernateSession.close();
            } catch (Exception e2)
            {
                // Silent failure of session close
            }
        }
    }

Listing 3.17 shows the use of the Criteria API to fetch both the Artifact and all of the associated Owners with a single SQL statement. For more information on the Criteria API, see Chapter 8.

Example 3.17. Loading the Artifacts and Owners

    public static void loadAllArtifacts()
    {
        System.out.println();
        System.out.println("loadAllArtifacts");

        Session hibernateSession = null;
        Transaction myTransaction = null;

        try
        {
            hibernateSession = sessionFactory.openSession();
            myTransaction =
                 hibernateSession.beginTransaction();

            // In this example, we use the Criteria API.
            // We could also have used the HQL, but the
            // Criteria API allows us to express this
            // query more easily. First indicate that
            // we want to grab all of the artifacts.
            Criteria query =
                hibernateSession.createCriteria(Artifact.class);

            // Now, specify sorting by the artifact title
            query.addOrder(Order.asc("title"));

            // Indicate that we want to grab all of the
            // associated owners of artifacts as well.
            // This lets us pull of the data in a single
            // SQL statement!
            query.setFetchMode("owners", FetchMode.EAGER);

            // This actually performs the database request,
            // based on the query we've built.
            java.util.Iterator results =
                 query.list().iterator();
                      Artifact myArtifact;

            // Because we are grabbing all of the artifacts and
            // artifact owners, we need to store the returned
            // artifacts

            java.util.LinkedList retrievedArtifacts =
                new java.util.LinkedList();
            while (results.hasNext())
            {
                // Note that the result set is cast to the
                // Animal object directly—no manual
                // binding required.
                myArtifact = (Artifact)results.next();
                if (!retrievedArtifacts.contains(myArtifact))
                    retrievedArtifacts.add(myArtifact);

                update_id = myArtifact.getId();
            }

            java.util.Iterator results2 =
                retrievedArtifacts.iterator();
            while (results2.hasNext())
            {
                myArtifact = (Artifact)results2.next();
                if (false)
                {
                    System.out.println(myArtifact.getId() + "");
                    System.out.println(myArtifact.getTitle() +
                         "");
                }
                java.util.Set myOwners =
                    myArtifact.getOwners();
                java.util.Iterator myOwnersIterator =
                    myOwners.iterator();
                if (false)
                    while (myOwnersIterator.hasNext())
                        System.out.println(
                            ((Owner)myOwnersIterator.next())
                                .getName());
            }

            myTransaction.commit();
        } catch (Exception e)
        {
            e.printStackTrace();
            try
            {
                myTransaction.rollback();
            } catch (Exception e2)
            {
                // Silent failure of transaction rollback
            }
        } finally
        {
            try
            {
                if (hibernateSession != null)
                    hibernateSession.close();
            } catch (Exception e)
            {
                // Silent failure of session close
            }
        }
    }

Listing 3.18 shows a single object, loaded and then updated after a minor change.

Example 3.18. Updating a Single Object

    public static void updateOneObject()
    {
        System.out.println();
        System.out.println("updateOneObject");

        Session hibernateSession = null;
        Transaction myTransaction = null;

        try
        {
            hibernateSession = sessionFactory.openSession();
            myTransaction = hibernateSession.beginTransaction();

            Artifact myArtifact =
                (Artifact)hibernateSession.get(
                    Artifact.class,
                    new Long(update_id));

            myArtifact.setTitle("Test");
            hibernateSession.update(myArtifact);

            myTransaction.commit();

        } catch (Exception e)
        {
            e.printStackTrace();
            try
            {
                myTransaction.rollback();
            } catch (Exception e2)
            {
                // Silent failure of transaction rollback
            }
        } finally
        {
            try
            {
                if (hibernateSession != null)
                    hibernateSession.close();
            } catch (Exception e)
            {
                // Silent failure of session close
            }
        }
    }

Listing 3.19 shows the deletion of a set of objects that meet a particular HQL query (in this case, items that happen to have a title starting with “Golden”).

Note the commented-out call to Session.find() prior to the Session.delete(). Whenever you are deleting objects, it's a good idea to test this out in advance, making sure that the objects being deleted are the objects you actually want to delete.

Example 3.19. Deleting Particular Objects

    public static void deleteObjects()
    {
        System.out.println();
        System.out.println("deleteObjects");

        Session hibernateSession = null;
        Transaction myTransaction = null;
        try
        {
            hibernateSession = sessionFactory.openSession();
            myTransaction =
                 hibernateSession.beginTransaction();

            String hql =
                "from Artifact as artifact where "
                    + "artifact.title like 'Golden%'";

            java.util.List myArtifacts =
                 hibernateSession.find(hql);

            hibernateSession.delete(hql);

            hibernateSession.flush();

            String sql =
         "DELETE ownership FROM ownership LEFT JOIN artifact "
         + "ON ownership.artifact_id=artifact.id "
         + "WHERE artifact.id IS NULL";

            Connection myConnection =
                 hibernateSession.connection();
            Statement myStatement =
                 myConnection.createStatement();
            System.out.println(myStatement.execute(sql));
            System.out.println(myStatement.getUpdateCount());
            myTransaction.commit();

        } catch (Exception e)
        {
            e.printStackTrace();
            try
            {
                myTransaction.rollback();
            } catch (Exception e2)
            {
                // Silent failure of transaction rollback
            }
        } finally
        {
            try
            {
                if (hibernateSession != null)
                    hibernateSession.close();
            } catch (Exception e)
            {
                // Silent failure of session close
            }
        }
    }

You may sometimes wish to perform SQL operations on the database directly, instead of relying on HQL or the Criteria API. This is especially true when performing a mass insert, but Listing 3.20 shows the use of SQL to perform a bulk deletion.

Example 3.20. Deleting Many Objects at Once Using SQL

    public static void massDelete()
    {
        System.out.println();
        System.out.println("massDelete");
        Session hibernateSession = null;
        Transaction myTransaction = null;

        try
        {
            hibernateSession = sessionFactory.openSession();
            myTransaction =
                 hibernateSession.beginTransaction();
            Connection myConnection =
                 hibernateSession.connection();
            Statement myStatement =
                 myConnection.createStatement();

            String sql = "delete from artifact";
            System.out.println(sql);
            myStatement.execute(sql);

            sql = "delete from owner";
            System.out.println(sql);
            myStatement.execute(sql);

            sql = "delete from ownership";
            System.out.println(sql);
            myStatement.execute(sql);
            myTransaction.commit();

        } catch (Exception e)
        {
            e.printStackTrace();
            try
            {
                myTransaction.rollback();
            } catch (Exception e2)
            {
                // Silent failure of transaction rollback
            }
        } finally
        {
            try
            {
                if (hibernateSession != null)
                    hibernateSession.close();
            } catch (Exception e)
            {
                // Silent failure of session close
            }
        }

    }

}

Assuming that everything is working properly, the output of this test application should be as shown in Listing 3.21.

NOTE

The final step of this program is the deletion of all the data in the tables—you may wish to comment out the references to the deleteObjects() and/or mass Delete() methods in the main() method to explore the data generated in the database.

Example 3.21. Successful Output

Current count: 0
initialization OK

createObjects

Current count: 5
createObjects OK

loadAllArtifacts

Current count: 5
loadAllArtifacts OK

updateOneObject

Current count: 5
updateOneObject OK

deleteObjects
false
3

Current count: 4
deleteObjects OK

massDelete
delete from artifact
delete from owner
delete from ownership

Current count: 0
massDelete OK
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.142.230