It is often necessary to perform various actions at different stages of a persistent object's lifecycle. JPA includes a variety of callback methods for monitoring changes in the lifecycle of your persistent objects. These callbacks can be defined on the persistent classes themselves and on non-persistent listener classes.
Every persistence event has a corresponding callback method marker. These markers are shared between persistent classes and their listeners. You can use these markers to designate a method for callback either by annotating that method or by listing the method in the XML mapping file for a given class. The lifecycle events and their corresponding method markers are:
PrePersist
:
Methods marked with this annotation will be invoked
before an object is persisted. This could be used for
assigning primary key values to persistent objects.
This is equivalent to the XML element tag
pre-persist
.
PostPersist
:
Methods marked with this annotation will be invoked
after an object has transitioned to the persistent state.
You might want to use such methods to update a screen
after a new row is added. This is equivalent
to the XML element tag post-persist
.
PostLoad
:
Methods marked with this annotation will be invoked after
all eagerly fetched fields of your class have been
loaded from the datastore. No other persistent fields
can be accessed in this method. This is equivalent
to the XML element tag post-load
.
PostLoad
is often used to
initialize non-persistent fields whose values depend
on the values of persistent fields, such as a complex
datastructure.
PreUpdate
:
Methods marked with this annotation will be invoked
just before the persistent values in your objects are flushed
to the datastore. This is equivalent to the XML element tag
pre-update
.
PreUpdate
is the complement to
PostLoad
. While methods marked
with PostLoad
are most often used
to initialize non-persistent values from persistent data,
methods annotated with PreUpdate
is normally used to set persistent fields with information
cached in non-persistent data.
PostUpdate
:
Methods marked with this annotation will be invoked
after changes to a given instance have been stored in the
datastore. This is useful for clearing stale data cached
at the application layer. This is equivalent to the
XML element tag post-update
.
PreRemove
:
Methods marked with this annotation will be invoked
before an object transactions to the deleted state.
Access to persistent fields is valid within this method.
You might use this method to cascade the deletion to
related objects based on complex criteria, or to
perform other cleanup. This is equivalent to the
XML element tag pre-remove
.
PostRemove
:
Methods marked with this annotation will be invoked after
an object has been marked for deletion. This is
equivalent to the XML element tag
post-remove
.
When declaring callback methods on a persistent class, any method may be used which takes no arguments and is not shared with any property access fields. Multiple events can be assigned to a single method as well.
Below is an example of how to declare callback methods on persistent classes:
/** * Example persistent class declaring our entity listener. */ @Entity public class Magazine { @Transient private byte[][] data; @ManyToMany private List<Photo> photos; @PostLoad public void convertPhotos () { data = new byte[photos.size ()][]; for (int i = 0; i < photos.size (); i++) data[i] = photos.get (i).toByteArray (); } @PreDelete public void logMagazineDeletion () { getLog ().debug ("deleting magazine containing" + photos.size () + " photos."); } }
In an XML mapping file, we can define the same methods without annotations:
<entity class="Magazine"> <pre-remove>logMagazineDeletion</pre-remove> <post-load>convertPhotos</post-load> </entity>
Note | |
---|---|
We fully explore persistence metadata annotations and XML in Chapter 5, Metadata. |
Mixing lifecycle event code into your persistent classes is not
always ideal. It is often more elegant to handle cross-cutting
lifecycle events in a non-persistent listener class.
JPA allows for this, requiring only that listener
classes have a public no-arg constructor. Like persistent
classes, your listener classes can consume any number of callbacks.
The callback methods must take in a single
java.lang.Object
argument which
represents the persistent object that triggered the event.
Entities can enumerate listeners using the
EntityListeners
annotation. This annotation
takes an array of listener classes as its value.
Below is an example of how to declare an entity and its corresponding listener classes.
/** * Example persistent class declaring our entity listener. */ @Entity @EntityListeners({ MagazineLogger.class, ... }) public class Magazine { // ... // } /** * Example entity listener. */ public class MagazineLogger { @PostPersist public void logAddition (Object pc) { getLog ().debug ("Added new magazine:" + ((Magazine) pc).getTitle ()); } @PreRemove public void logDeletion (Object pc) { getLog ().debug ("Removing from circulation:" + ((Magazine) pc).getTitle ()); } }
In XML we define both the listeners and their callback methods this way:
<entity class="Magazine"> <entity-listeners> <entity-listener class="MagazineLogger"> <post-persist>logAddition</post-persist> <pre-remove>logDeletion</pre-remove> </entity-listener> </entity-listeners> </entity>
Entity listener methods are invoked in a specific order when a given event is fired. So-called default listeners are invoked first: these are listeners which have been defined in a package annotation or in the root element of XML mapping files. Next, entity listeners are invoked in the order of the inheritance hierarchy, with superclass listeners being invoked before subclass listeners. Finally, if an entity has multiple listeners for the same event, the listeners are invoked in declaration order.
You can exclude default listeners, and listeners defined in superclasses, from the invocation chain via two class-level annotations:
ExcludeDefaultListeners
: This
annotation indicates that no default listeners will be
invoked for this class, or any of its subclasses. The XML
equivalent is the empty
exclude-default-listeners
element.
ExcludeSuperclassListeners
: This
annotation will cause Kodo to skip invoking any listeners
declared in superclasses. The XML equivalent is empty the
exclude-superclass-listeners
element.