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

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