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

Life-Cycle Methods

Now that we have looked in depth at the various notions of identity, we can take advantage of the life-cycle methods built in to Hibernate to experiment with the various available systems.

There are two main interfaces of life-cycle interest, one for general notifications and one for validation. Let's start by looking at an example of life-cycle application. Figure 6.3 shows a persistent Java object, a simple message. Note the implementation of the net.sf.hibernate.Lifecycle and net.sf .hibernate.Validatable interfaces. These are used to flag that this class is interested both in events relating to the object's life cycle (e.g., creation, saving, updating, destruction) and in events providing an opportunity to validate the state of the object before certain operations (in particular, saving or updating the object).

Figure 6.3. Message


As you review the life-cycle methods shown below, keep in mind that the life-cycle events correspond to the terminology used at the start of this chapter. For example, the update event corresponds to when a transient object is rendered persistent, and not to all instances when a given persistent object is updated. Similarly, the validation methods are called when appropriate from a persistence perspective. Do not use these life-cycle methods to manage your business logicthe terminology and events are only relevant from a persistence model perspective.

The code for the message object, as shown in Listing 6.8 (with white space trimmed) is based on XDoclet (described in Chapter 3). In particular, note the methods implementing the various life-cycle interfaces. This class doesn't do anything very interesting (beyond echoing the fact of notification), but it's easy to envision possible uses.

Listing 6.8. Object Life-Cycle Example
package com.cascadetg.ch06;

import java.io.Serializable;

import net.sf.hibernate.CallbackException;
import net.sf.hibernate.Lifecycle;
import net.sf.hibernate.Session;
import net.sf.hibernate.Validatable;
import net.sf.hibernate.ValidationFailure;

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

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

public class Message
    implements Lifecycle, Validatable, Serializable
{

    String message;

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

    /**
     * @param comments
     * The comments to set.
     */
    public void setMessage(String message)
    { this.message = message; }
    String id;
    /**
     * @hibernate.id generator-class="uuid.hex"
     * Xhibernate.id generator-class="native"
     * Xhibernate.id generator-class="increment"
     *
     * @return Returns the id.
     */
    public String getId() { return id; }

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

    /* see net.sf.hibernate.Lifecycle#onSave(net.sf.hibernate.Session)
     */
    public boolean onSave(Session s) throws CallbackException
    {
        echoIdentity();
        System.out.println("saved.");
        return Lifecycle.NO_VETO;
    }
    /* see net.sf.hibernate.Lifecycle#onUpdate(
         net.sf.hibernate.Session)
    */
    public boolean onUpdate(Session s) throws CallbackException
    {
        echoIdentity();
        System.out.println("updated.");
        return Lifecycle.NO_VETO;
    }

    /* see net.sf.hibernate.Lifecycle#onDelete(
         net.sf.hibernate.Session)
    */
    public boolean onDelete(Session s) throws CallbackException
    {
        echoIdentity();
        System.out.println("deleted.");
        return Lifecycle.NO_VETO;
    }
    /* see net.sf.hibernate.Lifecycle#onLoad(
         net.sf.hibernate.Session,
           java.io.Serializable)
    */
    public void onLoad(Session s, Serializable id)
    {
        echoIdentity();
        System.out.println("loaded.");
    }

    /* see net.sf.hibernate.Validatable#validate()
     */
    public void validate() throws ValidationFailure
    {
        echoIdentity();
        System.out.println("validated.");
    }

    /** Standard Hibernate equality override */
    public boolean equals(Object other)
    {
        if (!(other instanceof Message))
            return false;
        Message castOther = (Message)other;
        return new EqualsBuilder()
            .append(this.getId(), castOther.getId())
            .isEquals();
    }

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

    /** Not certain to work on all JVMs */
    public String jvmIdentity() { return super.toString(); }

    public void echoIdentity()
    {
        System.out.print("JVM:" + this.jvmIdentity());
        System.out.print(", KEY:" + this.getId());
        System.out.print(" HASH:" + this.hashCode() + " ");
    }

}

Executing the build file for this class, a *.hbm.xml file is generated by XDoclet, as shown in Listing 6.9 (white space edited).

Listing 6.9. Message Mapping
<?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.ch06.Message"
        dynamic-update="false"
        dynamic-insert="false" >

        <id name="id" column="id" type="java.lang.String" >
            <generator class="uuid.hex">
            </generator>
        </id>

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

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

    </class>

</hibernate-mapping>

Given the Java object and the mapping file, a simple test harness can be used to exercise the object (as shown in Listing 6.10). You'll notice that this class echoes the results of various object identity, validation, and life-cycle features of Hibernate.

Listing 6.10. Testing Life Cycles, Validation, and Identity
package com.cascadetg.ch06;

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

public class MessageTest
{
    public static void main(String[] args)
    {
        initialization();
        createMessage();
        loadMessage();
        updateMessage();
//        deleteMessage();
    }


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

    /** 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(Message.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();
        }
    }

    static String savedMessageID = null;

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

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

            Message newMessage = new Message();
            newMessage.echoIdentity();
            System.out.println("pre-set");

            newMessage.setId(savedMessageID);
            newMessage.echoIdentity();
            System.out.println("pre-load");

            // This object is not "owned" by Hibernate,
            // so the onUpdate() method IS called.
            hibernateSession.load(
                Message.class,
                newMessage.getId());

            hibernateSession.flush();
            myTransaction.commit();

            newMessage.echoIdentity();
            System.out.println("post-load");

        } 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
            }
        }

    }

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

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

            Message newMessage = new Message();
            newMessage.setId(savedMessageID);
            newMessage.setMessage("updated");

            // This object is not "owned" by Hibernate,
            // so the onUpdate() method IS called.
            hibernateSession.update(newMessage);

            hibernateSession.flush();

            newMessage.setMessage("indigo");
            hibernateSession.save(newMessage);

            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
            }
        }

    }

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

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

            Message newMessage = new Message();
            newMessage.setId(savedMessageID);

            hibernateSession.delete(newMessage);

            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
            }
        }

    }

    public static void createMessage()
    {
        System.out.println();
        System.out.println("createMessage");
        Session hibernateSession = null;
        Transaction myTransaction = null;
        try
        {
            hibernateSession = sessionFactory.openSession();
            myTransaction = hibernateSession.beginTransaction();

            Message myMessage = new Message();
            myMessage.setMessage("foo");

            hibernateSession.save(myMessage);

            hibernateSession.flush();
            myTransaction.commit();

            System.out.println(
                "New message id: " + myMessage.getId());

            myTransaction = hibernateSession.beginTransaction();

            // This object is already "owned" by Hibernate,
            // so the onUpdate() method is NOT called.
            myMessage.setMessage("bar");
            hibernateSession.update(myMessage);

            hibernateSession.flush();
            myTransaction.commit();

            // Save the message ID for later use.
            savedMessageID = myMessage.getId();

        } 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
            }
        }
    }
}

When run, the application produces output similar to that shown in Listing 6.11. You may wish to compare the values below to the results of the application as run on your system to verify aspects of identity as described earlier in the chapter. In addition, note the various validation and life-cycle method notifications, and compare the messages echoed to the state of the objects as they correspond to the source shown in Listing 6.9 and Listing 6.10. Finally, you may wish to change the identity and generator values of the Message object to see how different systems implement identity.

Listing 6.11. Results of the Identity Test Suite
initialization

createMessage
JVM:com.cascadetg.ch06.Message@484861a2, KEY:8a8092dcfbf4fd0100fbf4fd05eb0001 HASH
:1212703138 saved.
JVM:com.cascadetg.ch06.Message@484861a2, KEY:8a8092dcfbf4fd0100fbf4fd05eb0001 HASH
:1212703138 validated.
New message id: 8a8092dcfbf4fd0100fbf4fd05eb0001
JVM:com.cascadetg.ch06.Message@484861a2, KEY:8a8092dcfbf4fd0100fbf4fd05eb0001 HASH
:1212703138 validated.

loadMessage
JVM:com.cascadetg.ch06.Message@275, KEY:null HASH:629 pre-set
JVM:com.cascadetg.ch06.Message@484861a2, KEY:8a8092dcfbf4fd0100fbf4fd05eb0001 HASH
:1212703138 pre-load
JVM:com.cascadetg.ch06.Message@484861a2, KEY:8a8092dcfbf4fd0100fbf4fd05eb0001 HASH
:1212703138 loaded.
JVM:com.cascadetg.ch06.Message@484861a2, KEY:8a8092dcfbf4fd0100fbf4fd05eb0001 HASH
:1212703138 post-load

updateMessage
JVM:com.cascadetg.ch06.Message@484861a2, KEY:8a8092dcfbf4fd0100fbf4fd05eb0001 HASH
:1212703138 updated.
JVM:com.cascadetg.ch06.Message@484861a2, KEY:8a8092dcfbf4fd0100fbf4fd05eb0001 HASH
:1212703138 validated.
JVM:com.cascadetg.ch06.Message@484861a2, KEY:8a8092dcfbf4fd0100fbf4fd05eb0001 HASH
:1212703138 validated.