Chapter 5. Entity listeners and Callback methods

5.1. Definition

It is often useful for the application to react to certain events that occur inside the persistence mechanism. This allows the implementation of certain kinds of generic functionality, and extension of built-in functionality. The EJB3 specification provides two related mechanisms for this purpose.

A method of the entity may be designated as a callback method to receive notification of a particular entity life cycle event. Callbacks methods are annotated by a callback annotation. You can also define an entity listener class to be used instead of the callback methods defined directly inside the entity class. An entity listener is a stateless class with a no-arg constructor. An entity listener is defined by annotating the entity class with the @EntityListeners annotation:

@Entity 
@EntityListeners(class=Audit.class)
public class Cat {
    @Id private Integer id;
    private String name;
    private Calendar dateOfBirth;
    @Transient private int age;
    private Date lastUpdate;
    //getters and setters

    /**
     * Set my transient property at load time based on a calculation,
     * note that a native Hibernate formula mapping is better for this purpose.
     */
    @PostLoad
    public void calculateAge() {
        Calendar birth = new GregorianCalendar();
        birth.setTime(dateOfBirth);
        Calendar now = new GregorianCalendar();
        now.setTime( new Date() );
        int adjust = 0;
        if ( now.get(Calendar.DAY_OF_YEAR) - birth.get(Calendar.DAY_OF_YEAR) < 0) {
            adjust = -1;
        }
        age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR) + adjust;
    }
}

public class LastUpdateListener {
    /**
     * automatic property set before any database persistence
     */
    @PreUpdate
    @PrePersist
    public void setLastUpdate(Cat o) {
        o.setLastUpdate( new Date() );
    }
}

The same callback method or entity listener method can be annotated with more than one callback annotation. For a given entity, you cannot have two methods being annotated by the same callback annotation whether it is a callback method or an entity listener method. A callback method is a no-arg method with no return type and any arbitrary name. An entity listener has the signature void <METHOD>(Object) where Object is of the actual entity type (note that Hibernate Entity Manager relaxed this constraint and allows Object of java.lang.Object type (allowing sharing of listeners accross several entities.)

A callback method can raise a RuntimeException. The current transaction, if any, must be rolled back. The following callbacks are defined:

Table 5.1. Callbacks

TypeDescription
@PrePersistExecuted before the entity manager persist operation is actually executed or cascaded. This call is synchronous with the persist operation.
@PreRemoveExecuted before the entity manager remove operation is actually executed or cascaded. This call is synchronous with the remove operation.
@PostPersistExecuted after the entity manager persist operation is actually executed or cascaded. This call is invoked after the database INSERT is executed.
@PostRemoveExecuted after the entity manager remove operation is actually executed or cascaded. This call is synchronous with the remove operation.
@PreUpdateExecuted before the database UPDATE operation.
@PostUpdateExecuted after the database UPDATE operation.
@PostLoadEexecuted after an entity has been loaded into the current persistence context or an entity has been refreshed.

A callback method must not invoke EntityManager or Query methods!

5.2. Callbacks and listeners inheritance

You can define several entity listeners per entity at different level of the hierarchy.You can also define several callbacks at different level of the hierarchy. But you cannot define two listeners for the same event in the same entity or the same entity listener.

When an event is raised, the listeners are executed in this order:

  • @EntityListeners for a given entity or superclass in the array order

  • Entity listeners for the superclasses (highest first)

  • Entity Listeners for the entity

  • Callbacks of the superclasses (highest first)

  • Callbacks of the entity

You can stop the entity listeners inheritance by using the @ExcludeSuperclassListeners, all superclasses @EntityListeners will then be ignored.

5.3. XML definition

The EJB3 specification allows annotation overriding through EJB3 deployment descriptor. There is also an additional feature that can be useful: default event listeners.

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
                 version="1.0"
        >
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <entity-listeners>
                <entity-listener class="org.hibernate.ejb.test.pack.defaultpar.IncrementListener">
                    <pre-persist method-name="increment"/>
                </entity-listener>
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
    <package>org.hibernate.ejb.test.pack.defaultpar</package>
    <entity class="ApplicationServer">
        <entity-listeners>
            <entity-listener class="OtherIncrementListener">
                <pre-persist method-name="increment"/>
            </entity-listener>
        </entity-listeners>


        <pre-persist method-name="calculate"/>
    </entity>
</entity-mappings>

You can override entity listeners on a given entity. An entity listener correspond to a given class and one or several event fire a given method call. You can also define event on the entity itself to describe the callbacks.

Last but not least, you can define some default entity listeners that will apply first on the entity listener stack of all the mapped entities of a given persistence unit. If you don't want an entity to inherit the default listeners, you can use @ExcludeDefaultListeners (or <exclude-default-listeners/>).