cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Cayenne Documentation > Lifecycle Callbacks
Date Sun, 04 Oct 2009 15:21:00 GMT
<html>
<head>
    <base href="http://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/1519/1/1/_/styles/combined.css?spaceKey=CAYDOC&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background-color: white" bgcolor="white">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
     <h2><a href="http://cwiki.apache.org/confluence/display/CAYDOC/Lifecycle+Callbacks">Lifecycle
Callbacks</a></h2>
     <h4>Page <b>edited</b> by             <a href="http://cwiki.apache.org/confluence/display/~andrus">Andrus
Adamchik</a>
    </h4>
     
          <br/>
     <div class="notificationGreySide">
         <h2><a name="LifecycleCallbacks-LifecycleCallbacks"></a>Lifecycle
Callbacks</h2>

<p>Users can register callback methods that will be invoked during the lifecycle of
persistent objects. Callback mechanism is similar to the one defined in the <a href="/confluence/display/CAYJPA/JPA+Guide"
title="JPA Guide">JPA Specification</a>, however there are some noteable differences
introduced to better follow the Cayenne object lifecycle. There are eight lifecycle callbacks
described below (PostAdd, PrePersist, PostPersist, PreUpdate, PostUpdate, PreRemove, PostRemove,
PostLoad). Each one cab be invoked as a <b>callback on a persistent object</b>
itself or as a <b>callback on an arbitrary listener object</b>.</p>

<div class='panelMacro'><table class='noteMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/warning.gif" width="16" height="16"
align="absmiddle" alt="" border="0"></td><td><b>Callbacks feature supercedes
the following 1.2/2.0 features:</b><br /><ul>
	<li>Interception of object state transitions inside <tt>"Persistent.setPersistenceState()"</tt>.</li>
	<li>Event mechanism defined in <tt>"org.apache.cayenne.access.event"</tt>
package that was removed in 3.0.</li>
	<li><tt>"DataObject.validateForX"</tt> - it is a good idea to use it strictly
for validation; updating the state before commit should be done via callbacks.</li>
	<li><tt>"DataObject.fetchFinished()"</tt> - scheduled for removal in 3.0</li>
</ul>
</td></tr></table></div>

<h2><a name="LifecycleCallbacks-CallbackMethodSemantics"></a>Callback Method
Semantics</h2>

<ul>
	<li>No formal interface is required to mark a method to be used for callback (although
<tt>org.apache.cayenne.LifecycleListener</tt> can optionally be implemented for
type-safety and to simplify registration).</li>
	<li>A callback method signature looks like <tt>"void someMethod()"</tt>
<em>for persistent classes</em>.</li>
	<li>It looks like <tt>"void method(Type entityObject)"</tt> <em>for
listener classes</em>.</li>
	<li>A callback method can have an arbitrary name.</li>
	<li>A callback method can use public, private, protected or default access.</li>
	<li>It must NOT be static.</li>
	<li>Callback methods are polymorphic - registering a callback on a superclass (even
if the superclass does not map to an entity) will ensure the callback will be invoked on all
entity subclasses, using the overriding subclass method if applicable.</li>
</ul>


<p>Callback on persistent object example:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">public</span> class Artist
{ 
   ...

   <span class="code-comment">// a valid callback method
</span>   <span class="code-keyword">protected</span> void setDefaultProperties()
{
      ...
   }
}</pre>
</div></div>


<p>Callback on a listener class example:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">public</span> class MyListener
{ 
   ...

   <span class="code-comment">// a valid callback method
</span>   <span class="code-keyword">public</span> void initArtist(Artist
a) {
      ...
   }
}</pre>
</div></div>

<p>Note that empty callback methods are generated as a part of persistent class generation
(either with <a href="/confluence/display/CAYDOC/maven2-cgen" title="maven2-cgen">Maven</a>,
<a href="/confluence/display/CAYDOC/cgen" title="cgen">Ant</a> or CayenneModeler).</p>

<h2><a name="LifecycleCallbacks-TypesofCallbacks"></a>Types of Callbacks</h2>

<p>Valid callback types are defined as Java enumerated constants in the <tt>org.apache.cayenne.map.LifecycleEvent</tt>
enumeration.</p>

<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'>Callback</th>
<th class='confluenceTh'>Invoked...</th>
</tr>
<tr>
<td class='confluenceTd'>PostAdd</td>
<td class='confluenceTd'>Within <tt>"ObjectContext.newObject()"</tt> after
ObjectId and ObjectContext are set.</td>
</tr>
<tr>
<td class='confluenceTd'>PrePersist</td>
<td class='confluenceTd'>Prior to commit (and prior to "validateFor*") within <tt>"ObjectContext.commitChanges()"</tt>
and <tt>"ObjectContext.commitChangesToParent()"</tt></td>
</tr>
<tr>
<td class='confluenceTd'>PreRemove</td>
<td class='confluenceTd'>Before an object is deleted inside <tt>"ObjectContext.deleteObject()"</tt>;
also includes all objects that will be deleted as a result of CASCADE delete rule.</td>
</tr>
<tr>
<td class='confluenceTd'>PreUpdate</td>
<td class='confluenceTd'>Prior to commit (and prior to "validateFor*") within <tt>"ObjectContext.commitChanges()"</tt>
and <tt>"ObjectContext.commitChangesToParent()"</tt></td>
</tr>
<tr>
<td class='confluenceTd'>PostPersist</td>
<td class='confluenceTd'>Within <tt>"ObjectContext.commitChanges()"</tt>,
after commit of a new object is done.</td>
</tr>
<tr>
<td class='confluenceTd'>PostRemove</td>
<td class='confluenceTd'>Within <tt>"ObjectContext.commitChanges()"</tt>,
after commit of a deleted object is done.</td>
</tr>
<tr>
<td class='confluenceTd'>PostUpdate</td>
<td class='confluenceTd'>Within <tt>"ObjectContext.commitChanges()"</tt>,
after commit of a modified object is done.</td>
</tr>
<tr>
<td class='confluenceTd'>PostLoad</td>
<td class='confluenceTd'><ul>
	<li>Within <tt>"ObjectContext.performQuery()"</tt> after the object is
fetched.</li>
	<li>Within <tt>"ObjectContext.rollbackChanges()"</tt> after the object
is reverted.</li>
	<li>Anytime a faulted object is resolved (i.e. if a relationship is fetched.</li>
</ul>
</td>
</tr>
</tbody></table>

<h2><a name="LifecycleCallbacks-RegisteringCallbacks"></a>Registering Callbacks</h2>

<p>Normally listeners and persistent object callbacks are mapped in the Modeler, but
this can also be done in the code. Callbacks are registered with <tt>LifecycleCallbackRegistry</tt>,
which is shared by all contexts within a DataDomain.</p>

<p>Obtaining the shared registry instance:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">import</span> org.apache.cayenne.reflect.LifecycleCallbackRegistry;
...
DataDomain domain = ...
LifecycleCallbackRegistry registry = domain.getEntityResolver().getCallbackRegistry();</pre>
</div></div>


<p>Registry obtained this way already contains callbacks mapped in the DataMap. To add
extra callbacks in runtimes, use various <tt>addListener(...)</tt>  methods. </p>

<p>Adding a listener object that implements <tt>LifecycleListener</tt> interface:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">import</span> org.apache.cayenne.LifecycleListener

<span class="code-keyword">public</span> class MyListener <span class="code-keyword">implements</span>
LifecycleListener {
	<span class="code-keyword">public</span> void prePersist(<span class="code-object">Object</span>
entity) {
		Persistent p = (Persistent) entity;
	    <span class="code-object">System</span>.out.println(<span class="code-quote">"New
object created <span class="code-keyword">for</span> entity "</span> + p.getObjectId().getEntityName());

	}
	
	... 
}

<span class="code-comment">// listen <span class="code-keyword">for</span>
events on a single entity - Artist
</span>registry.addListener(Artist.class, <span class="code-keyword">new</span>
MyListener());

<span class="code-comment">// listen <span class="code-keyword">for</span>
events on ALL entities:
</span>registry.addDefaultListener(<span class="code-keyword">new</span>
MyListener());</pre>
</div></div>

<p>Adding a listener of an arbitrary class</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">public</span> class MyOtherListener
{
	
	<span class="code-comment">// note that callback method doesn't have to be 
</span>	<span class="code-comment">// <span class="code-keyword">public</span>
or called any predefined name
</span>    void onEntityLoad(<span class="code-object">Object</span> entity)
{
		Persistent p = (Persistent) entity;
	    <span class="code-object">System</span>.out.println(<span class="code-quote">"<span
class="code-object">Object</span> fetched: "</span> + p.getObjectId().getEntityName());

	}
	
	<span class="code-comment">// also we can pass the object already <span class="code-keyword">cast</span>
to the entity class 
</span>	<span class="code-comment">// <span class="code-keyword">if</span>
the method is only going to handle <span class="code-keyword">this</span> type
of entities
</span>	void onArtistChange(Artist artist) {
	    <span class="code-object">System</span>.out.println(<span class="code-quote">"Artist
changed "</span> + artist.getArtistName());	
	}
	
	... 
}

<span class="code-object">Object</span> listener = <span class="code-keyword">new</span>
MyOtherListener();

<span class="code-comment">// listen <span class="code-keyword">for</span>
different events on a single entity - Artist. The same
</span><span class="code-comment">// method can be registered <span class="code-keyword">for</span>
multiple events
</span>registry.addListener(LifecycleListener.PRE_PERSIST, Artist.class, listener, <span
class="code-quote">"onArtistChange"</span>);
registry.addListener(LifecycleListener.PRE_REMOVE, Artist.class, listener, <span class="code-quote">"onArtistChange"</span>);
registry.addListener(LifecycleListener.PRE_UPDATE, Artist.class, listener, <span class="code-quote">"onArtistChange"</span>);

<span class="code-comment">// register another method to listen <span class="code-keyword">for</span>
ALL entities
</span>registry.addListener(LifecycleListener.POST_LOAD, listener, <span class="code-quote">"onEntityLoad"</span>);</pre>
</div></div>

<p>Finally a persistent object can implement callbacks as well, being notified of its
own events:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-comment">// <span class="code-quote">"<span
class="code-keyword">extends</span> _Artist"</span> implies <span class="code-quote">"<span
class="code-keyword">implements</span> Persistent"</span> via a superclass
</span><span class="code-keyword">public</span> class Artist <span class="code-keyword">extends</span>
_Artist {
	
	<span class="code-comment">// note that callback on entity is a no-arg method.
</span>	void onLoad() {
		Persistent p = (Persistent) entity;
	    <span class="code-object">System</span>.out.println(<span class="code-quote">"<span
class="code-object">Object</span> fetched: "</span> + <span class="code-keyword">this</span>);

	}
}

<span class="code-comment">// register Artist class callback
</span>registry.addListener(LifecycleListener.POST_LOAD, Artist.class, <span class="code-quote">"onLoad"</span>);</pre>
</div></div>

     </div>
     <div id="commentsSection" class="wiki-content pageSection">
       <div style="float: right;">
            <a href="http://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
       </div>

       <a href="http://cwiki.apache.org/confluence/display/CAYDOC/Lifecycle+Callbacks">View
Online</a>
       |
       <a href="http://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=26244&revisedVersion=28&originalVersion=27">View
Change</a>
              |
       <a href="http://cwiki.apache.org/confluence/display/CAYDOC/Lifecycle+Callbacks?showComments=true&amp;showCommentArea=true#addcomment">Add
Comment</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message