Cayenne User Documentation
Lifecycle Callbacks

Lifecycle Callbacks

(since 3.0M1)

TODO: annotations, Modeler support

Users can register callback methods that will be invoked during the lifecycle of persistent objects. Callback mechanism matches closely the one defined in the JPA Specification (except that it works with JDK 1.4 and allows preconfigured listeners). There are seven lifecycle callbacks described below (PrePersist, PostPersist, PreUpdate, PostUpdate, PreRemove, PostRemove, PostLoad). There are two types of invocations for each one of them: callback on a persistent object itself or a callback on an arbitrary listener object.

Callbacks feature supercedes the following 1.2/2.0 features:
  • Interception of object state transitions inside "Persistent.setPersistenceState()".
  • Event mechanism defined in "org.apache.cayenne.access.event" package. Scheduled for removal in 3.0.
  • "DataObject.validateForX" - it is a good idea to use it strictly for validation; updating the state before commit should be done via callbacks.
  • "DataObject.fetchFinished()" - scheduled for removal in 3.0

Callback Method Semantics

Callback on persistent object example:

public class Artist { 
   ...

   // a valid callback method
   protected void setDefaultProperties() {
      ...
   }
}

Callback on a listener class example:

public class MyListener { 
   ...

   // a valid callback method
   public void initArtist(Artist a) {
      ...
   }
}

Types of Callbacks

Valid callback types are defined as int constants in the LifecycleListener interface.

Callback Invoked...
PrePersist Within "ObjectContext.newObject()" after ObjectId and ObjectContext are set.
PreRemove Before an object is deleted inside "ObjectContext.deleteObject()"; also includes all objects that will be deleted as a result of CASCADE delete rule.
PreUpdate Prior to commit (and prior to "validateFor*") within "ObjectContext.commitChanges()" and "ObjectContext.commitChangesToParent()"
PostPersist Within "ObjectContext.commitChanges()", after commit of a new object is done.
PostRemove Within "ObjectContext.commitChanges()", after commit of a deleted object is done.
PostUpdate Within "ObjectContext.commitChanges()", after commit of a modified object is done.
PostLoad
  • Within "ObjectContext.performQuery()" after the object is fetched.
  • Within "ObjectContext.rollbackChanges()" after the object is reverted.
  • Anytime a faulted object is resolved (i.e. if a relationship is fetched.

Registering Callbacks

Listeners and persistent object callbacks can be mapped in the Modeler (TODO: GUI not implemented yet)_

Alternatively callbacks can be registered with LifecycleCallbackRegistry, which is shared by all contexts within DataDomain.

Obtaining the shared registry instance:

import org.apache.cayenne.reflect.LifecycleCallbackRegistry;
...
DataDomain domain = ...
LifecycleCallbackRegistry registry = domain.getEntityResolver().getCallbackRegistry();

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

Adding a listener object that implements LifecycleListener interface:

import org.apache.cayenne.LifecycleListener

public class MyListener implements LifecycleListener {
	public void prePersist(Object entity) {
		Persistent p = (Persistent) entity;
	    System.out.println("New object created for entity " + p.getObjectId().getEntityName());	
	}
	
	... 
}

// listen for events on a single entity - Artist
registry.addListener(Artist.class, new MyListener());

// listen for events on ALL entities:
registry.addDefaultListener(new MyListener());

Adding a listener of an arbitrary class

public class MyOtherListener {
	
	// note that callback method doesn't have to be 
	// public or called any predefined name
    void onEntityLoad(Object entity) {
		Persistent p = (Persistent) entity;
	    System.out.println("Object fetched: " + p.getObjectId().getEntityName());	
	}
	
	// also we can pass the object already cast to the entity class 
	// if the method is only going to handle this type of entities
	void onArtistChange(Artist artist) {
	    System.out.println("Artist changed " + artist.getArtistName());	
	}
	
	... 
}

Object listener = new MyOtherListener();

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

// register another method to listen for ALL entities
registry.addListener(LifecycleListener.POST_LOAD, listener, "onEntityLoad");

Finally a persistent object can implement callbacks as well, being notified of its own events:

// "extends _Artist" implies "implements Persistent" via a superclass
public class Artist extends _Artist {
	
	// note that callback on entity is a no-arg method.
	void onLoad() {
		Persistent p = (Persistent) entity;
	    System.out.println("Object fetched: " + this);	
	}
}

// register Artist class callback
registry.addListener(LifecycleListener.POST_LOAD, Artist.class, "onLoad");

Enabling Callbacks

TODO: expect this to be changed to something more user-friendly.

By default callbacks are disable. To building ObjectContext with enabled callbacks, do something like this:

DataChannelCallbackInterceptor postInterceptor = new DataChannelCallbackInterceptor();
postInterceptor.setChannel(domain);
ObjectStore objectStore = new ObjectStore(domain.getSharedSnapshotCache());

ObjectContextCallbackInterceptor preInterceptor = new ObjectContextCallbackInterceptor();
preInterceptor.setContext(new DataContext(postInterceptor, objectStore));

ObjectContext userContext = preInterceptor;
.